Flask

Chapter 5-4 Flask로 만드는 인스타그램 REST API

Chansman 2025. 4. 23. 10:26

📌 Mini Project: Flask로 만드는 인스타그램 REST API

이 프로젝트는 Flask를 이용해 간단한 REST API를 구현하여 인스타그램의 사용자 및 게시물 관리를 수행합니다.


🚦 구현해야 할 주요 API 목록

HTTP Method Endpoint 설명

GET /users 모든 사용자 조회
POST /users 새로운 사용자 생성
POST /users/post/<username> 사용자 게시물 추가
GET /users/post/<username> 사용자 게시물 목록 조회
PUT /users/post/like/<username>/<title> 특정 게시물의 좋아요 수 증가
DELETE /users/<username> 사용자 삭제

💻 구현 코드와 단계별 설명

app.py

from flask import Flask, request, render_template

app = Flask(__name__)


# 사용자 데이터를 저장하는 리스트
users = [
    {
        "username": "leo",
        "posts": [{"title": "Town House", "likes": 120}]
    },
    {
        "username": "alex",
        "posts": [{"title": "Mountain Climbing", "likes": 350}, {"title": "River Rafting", "likes": 200}]
    },
    {
        "username": "kim",
        "posts": [{"title": "Delicious Ramen", "likes": 230}]
    }
]

@app.get("/")
def index():
    # 모든 사용자 정보를 JSON 형태로 반환
    return render_template('index.html')

@app.get("/users")
def get_users():
    # 모든 사용자 정보를 JSON 형태로 반환
    return {"users": users}


@app.post("/users")
def create_user():
    # 클라이언트로부터 받은 JSON 데이터
    request_data = request.get_json()
    # 새 사용자 객체 생성
    new_user = {"username": request_data["username"], "posts": [{"title": "My First Post", "likes": 0}]}
    # 사용자 리스트에 새 사용자 추가
    users.append(new_user)
    # 생성된 사용자 정보와 HTTP 상태코드 201 반환
    return new_user, 201

@app.post("/users/post/<string:username>")
def add_post(username):
    # 클라이언트로부터 받은 JSON 데이터
    request_data = request.get_json()
    # 해당 사용자를 찾아 게시물 추가
    for user in users:
        if user["username"] == username:
            new_post = {"title": request_data["title"], "likes": request_data["likes"]}
            user["posts"].append(new_post)
            return new_post
        
    # 사용자를 찾지 못한 경우 오류 메시지 반환
    return {"message": "User not found"}, 404

@app.get("/users/post/<string:username>")
def get_posts_of_user(username):
    # 특정 사용자의 모든 게시물 반환
    for user in users:
        if user["username"] == username:
            return {"posts": user["posts"]}
    # 사용자를 찾지 못한 경우 오류 메시지 반환
    return {"message": "User not found"}, 404

@app.put("/users/post/like/<string:username>/<string:title>")
def like_post(username, title):
    # 특정 게시물의 좋아요 수 증가
    for user in users:
        if user["username"] == username:
            for post in user["posts"]:
                if post["title"] == title:
                    post["likes"] += 1
                    return post
    # 게시물을 찾지 못한 경우 오류 메시지 반환
    return {"message": "Post not found"}, 404

@app.delete("/users/<string:username>")
def delete_user(username):
    # 전역 users 리스트에서 특정 사용자 삭제
    global users
    users = [user for user in users if user["username"] != username]
    # 삭제 성공 메시지와 HTTP 상태코드 200 반환
    return {"message": "User deleted"}, 200

# 애플리케이션 실행
if __name__ == '__main__':
    app.run(debug=True)

 

index.html

<!DOCTYPE html>
<html lang="ko">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>사용자 관리 시스템</title>
    <script>
      // 사용자 목록을 요청하여 화면에 출력
      function getUsers() {
        fetch('/users')
          .then(response => response.json())
          .then(data => {
            let userList = document.getElementById('user-list');
            userList.innerHTML = '';  // 기존 목록 초기화
            data.users.forEach(user => {
              // 각 사용자와 해당 사용자의 게시물 제목을 출력
              let postTitles = user.posts.map(post => post.title).join(', ');
              userList.innerHTML += `<li>${user.username} - 게시물: ${postTitles}</li>`;
            });
          });
      }
      // 사용자 생성 요청
      function createUser() {
        const username = document.getElementById('new-username').value;
        fetch('/users', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ "username": username })
        })
          .then(response => response.json())
          .then(data => {
            alert('사용자가 생성되었습니다: ' + data.username);
            getUsers();
          });
      }

      // 특정 사용자에 게시물 추가
      function addPost() {
        const username = document.getElementById('post-username').value;
        const title = document.getElementById('post-title').value;
        const likes = parseInt(document.getElementById('post-likes').value, 10);
        fetch(`/users/post/${username}`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ "title": title, "likes": likes })
        })
          .then(response => response.json())
          .then(data => {
            alert('게시물이 추가되었습니다: ' + data.title);
            getPosts(username);
          });
      }

      // 특정 사용자의 게시물 조회
      function getPosts(username) {
        fetch(`/users/post/${username}`)
          .then(response => response.json())
          .then(data => {
            let postList = document.getElementById('post-list');
            postList.innerHTML = '';  // 기존 목록 초기화
            data.posts.forEach(post => {
              postList.innerHTML += `<li>${post.title} - 좋아요: ${post.likes}</li>`;
            });
          });
      }

      // 게시물 좋아요 수 증가
      function likePost() {
        const username = document.getElementById('like-username').value;
        const title = document.getElementById('like-title').value;
        fetch(`/users/post/like/${username}/${title}`, {
          method: 'PUT'
        })
          .then(response => response.json())
          .then(data => {
            alert('게시물의 좋아요가 증가되었습니다: ' + data.likes);
            getPosts(username);
          });
      }

      // 사용자 삭제 요청
      function deleteUser() {
        const username = document.getElementById('delete-username').value;
        fetch(`/users/${username}`, {
          method: 'DELETE'
        })
          .then(response => response.json())
          .then(data => {
            alert('사용자가 삭제되었습니다');
            getUsers();
          });
      }
    </script>
  </head>

  <body>
    <h1>사용자 관리 시스템</h1>

    <h2>1. 사용자 목록 조회</h2>
    <button onclick="getUsers()">사용자 목록 조회</button>
    <ul id="user-list"></ul>

    <h2>2. 사용자 생성</h2>
    <input type="text" id="new-username" placeholder="사용자 이름">
    <button onclick="createUser()">사용자 생성</button>

    <h2>3. 게시물 추가</h2>
    <input type="text" id="post-username" placeholder="사용자 이름">
    <input type="text" id="post-title" placeholder="게시물 제목">
    <input type="number" id="post-likes" placeholder="좋아요 수">
    <button onclick="addPost()">게시물 추가</button>

    <h2>4. 사용자별 게시물 조회</h2>
    <input type="text" id="get-posts-username" placeholder="사용자 이름">
    <button onclick="getPosts(document.getElementById('get-posts-username').value)">게시물 조회</button>
    <ul id="post-list"></ul>

    <h2>5. 게시물 좋아요 수 증가</h2>
    <input type="text" id="like-username" placeholder="사용자 이름">
    <input type="text" id="like-title" placeholder="게시물 제목">
    <button onclick="likePost()">좋아요 증가</button>

    <h2>6. 사용자 삭제</h2>
    <input type="text" id="delete-username" placeholder="사용자 이름">
    <button onclick="deleteUser()">사용자 삭제</button>
  </body>

</html>

📚 파이썬 리스트 컴프리헨션

리스트 컴프리헨션은 리스트를 간단하고 효율적으로 생성하는 방법입니다.

기본 형식:

[표현식 for 항목 in 반복가능객체 if 조건]

사용 예시:

예시 코드 설명 결과

[n ** 2 for n in [1,2,3,4,5]] 각 숫자를 제곱 [1, 4, 9, 16, 25]
[n*2 for n in [1,2,3,4,5] if n%2==1] 홀수만 선택해 두 배로 변환 [2, 6, 10]
[word.upper() for word in ["hello", "world"]] 모든 단어를 대문자로 변환 ["HELLO", "WORLD"]
[n**2 if n%2==0 else n*2 for n in [1,2,3]] 짝수는 제곱, 홀수는 두 배로 변환 [2, 4, 6]

🧪 실행 및 테스트 방법

  1. 서버 실행:
python app.py
  1. API 테스트 예시 (curl 이용):
  • 사용자 목록 조회:
curl http://127.0.0.1:5000/users
  • 사용자 추가:
curl -X POST -H "Content-Type: application/json" -d '{"username":"john"}' http://127.0.0.1:5000/users
  • 게시물 추가:
curl -X POST -H "Content-Type: application/json" -d '{"title":"Beach Trip","likes":100}' http://127.0.0.1:5000/users/post/john
  • 특정 게시물 좋아요 증가:
curl -X PUT http://127.0.0.1:5000/users/post/like/john/Beach%20Trip
  • 사용자 삭제:
curl -X DELETE http://127.0.0.1:5000/users/john

 

3개의 파일로 관리할경우 추가 2개 파일

user_routes.py - 사용자 관련 라우트 정의

from flask import request
from user_model import users, add_user, add_post_to_user, get_user_posts, like_user_post, delete_user

def register_routes(app):
    @app.route("/users", methods=["GET", "POST"])
    def users_route():
        if request.method == "GET":
            return {"users": users}
        elif request.method == "POST":
            request_data = request.get_json()
            return add_user(request_data)

    @app.route("/users/post/<string:username>", methods=["POST"])
    def add_post(username):
        request_data = request.get_json()
        return add_post_to_user(username, request_data)

    @app.route("/users/post/<string:username>", methods=["GET"])
    def get_posts(username):
        return get_user_posts(username)

    @app.route("/users/post/like/<string:username>/<string:title>", methods=["PUT"])
    def like_post(username, title):
        return like_user_post(username, title)

    @app.route("/users/<string:username>", methods=["DELETE"])
    def delete(username):
        return delete_user(username)

user_model.py - 사용자 데이터 및 관련 함수 정의

users = [
    {"username": "leo", "posts": [{"title": "Town House", "likes": 120}]},
    {"username": "alex", "posts": [{"title": "Mountain Climbing", "likes": 350}, {"title": "River Rafting", "likes": 200}]},
    {"username": "kim", "posts": [{"title": "Delicious Ramen", "likes": 230}]}
]

def add_user(request_data):
    new_user = {"username": request_data["username"], "posts": []}
    users.append(new_user)
    return new_user, 201

def add_post_to_user(username, request_data):
    for user in users:
        if user["username"] == username:
            new_post = {"title": request_data["title"], "likes": 0}
            user["posts"].append(new_post)
            return new_post, 201
    return {"message": "User not found"}, 404

def get_user_posts(username):
    for user in users:
        if user["username"] == username:
            return {"posts": user["posts"]}
    return {"message": "User not found"}, 404

def like_user_post(username, title):
    for user in users:
        if user["username"] == username:
            for post in user["posts"]:
                if post["title"] == title:
                    post["likes"] += 1
                    return post, 200
    return {"message": "Post not found"}, 404

def delete_user(username):
    global users
    users = [user for user in users if user["username"] != username]
    return {"message": "User deleted"}, 200

마무리 및 복습 포인트

  • Flask를 활용하여 간단한 REST API를 구현할 수 있습니다.
  • JSON 데이터 처리와 RESTful 라우팅 방법을 익힐 수 있습니다.
  • 리스트 컴프리헨션을 사용하면 코드가 간결해지고 효율성이 높아집니다.