⚡️ API 개발 with Flask - 4

codeamor·2020년 7월 6일
0

API Development

목록 보기
4/9

✔️ 팔로우와 언팔로우 엔드포인트

  • 다른 트위터들을 팔로우(언팔로우)하고 팔로우하는 사용자들의 글과 사진을 타임라인에서 볼 수 있는 기능
  • 팔로우(언팔로우)하고 싶은 사용자의 아이디를 HTTP 요청으로 보내면 API에서 해당 요청을 처리하는 방식으로 구현

전송할 JSON 데이터는 아래와 같다.

{
    "id"     : 1,
    "follow" : 2
}
{
    "id"       : 1,
    "unfollow" : 2
}

id 필드는 해당 사용자의 아이디이고, follow/unfollow 필드는 언팔로우하고자 하는 사용자의 아이디이다.

follow 엔드포인트

먼저 팔로우 엔드포인트부터 구현해본다.

@app.route('/follow', methods=['POST'])
def follow():
    payload           = request.json
    user_id           = int(payload['id'])
    user_id_to_follow = int(payload['follow'])

    if user_id not in app.users or user_id_to_follow not in app.users:
        return '유저가 존재 하지 않습니다', 400

    user = app.users[user_id]
    user.setdefault('follow', set()).add(user_id_to_follow)

    return jsonify(user)
  1. user_id = int(payload['id'])
    : HTTP 요청으로 전송된 JSON 데이터에서 해당 사용자의 아이디를 읽어 들인다.

  2. user_id_to_follow = int(payload['follow'])
    : HTTP 요청으로 전송된 JSON 데이터에서 해당 사용자가 팔로우할 사용자의 아이디를 읽어 들인다.

  3. if user_id not in app.users or user_id_to_follow not in app.users:
    return '유저가 존재 하지 않습니다', 400

    : 만일 해당 사용자나 팔로우할 사용자가 존재하지 않는다면 400 Bad Request 응답을 보낸다.

  4. user = app.users[user_id]
    : app.users 딕셔너리에서 해당 사용자 아이디를 사용해서 해당 사용자의 데이터를 읽어 들인다.

  5. user.setdefault('follow', set()).add(user_id_to_follow)
    : 4번에서 읽어 들인 사용자의 정보를 담고 있는 딕셔너리가 이미 "follow"라는 필드를 가지고 있다면, 즉 이미 사용자가 다른 사용자를 팔로우한 적이 있다면, 사용자의 "follow" 키와 연결되어 있는 set에 팔로우하고자 하는 사용자 아이디를 추가한다.

  • 만일 처음 다른 사용자를 팔로우 하는 것이라면 사용자의 정보를 담고 있는 딕셔너리에 "follow"라는 키를 empty set과 연결하여 추가한다.

    • 이처럼 키가 존재하지 않으면 디폴트 값을 저장하고, 키가 이미 존재하면 해당 값을 읽어 들이는 기능을 setdefault를 사용하여 구현한다.
  • 팔로우 엔드포인트를 구현할 때 해당ㅇ 사용자가 팔로우하는 다른 사용자들의 아이디를 저장하는 자료구조로써 set을 사용한다.

    • list를 안쓰고 set을 사용하는 이유는 동일한 사용자 아이디가 여러 번 저장되지 않게 해주기 때문이다.
    • set을 사용함으로써 팔로우하고자 하는 사용자 아이디가 이미 팔로우되고 있지는 않은지에 대한 확인을 굳이 해주지 않아도 된다.

서버를 실행시켜서 팔로우 엔드포인트에 HTTP 요청을 보내도록 하자

(id가 2개 필요하므로 회원가입 엔드포인트를 2번 실행시켜서 id를 2번까지 만들어줘야 한다.)

명령어를 실행시키면 위와 같은 서버 에러가 뜨게 되는데

이유는 팔로우하는 사용자 아이디들을 저장하는 자료구조로 사용하는 set을 파이썬의 json 모듈이 JSON으로 변경하지 못하기 때문이다.
list는 JSON으로 변경될 수 있지만 set은 변경하지 못하므로 오류가 난다.

이 문제를 해결하기 위해 커스텀 JSON 엔코더(custom JSON encoder)를 구현해서 디폴트 JSON 엔코더에 덮어 써야한다.

from flask.json import JSONEncoder

## Default JSON encoder는 set를 JSON으로 변환할 수 없다.
## 그러므로 커스텀 엔코더를 작성해서 set을 list로 변환하여
## JSON으로 변환 가능하게 해주어야 한다.
class CustomJSONEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, set):
            return list(obj)

        return JSONEncoder.default(self, obj)

app.json_encoder = CustomJSONEncoder
  1. from flask.json import JSONEncoder
    : flask.json 모듈에서 JSONEncoder 클래스를 임포트한다.
  • JSONEncoder 클래스를 확장해서 커스텀 엔코더를 구현한다.

  1. class CustomJSONEncoder(JSONEncoder):
    : JSONEncoder 클래스를 부모 클래스로 상속받는 CustomJSONEncoder 클래스를 정의한다.

  2. def default(self, obj):
    : JSONEncoder 클래스의 default 메소드를 확장(over-write)한다.
  • default 메소드에서 set인 경우 list로 변경해 주어야 한다.

  1. if isinstance(obj, set):
    : JSON으로 변경하고자 하는 객체(obj)가 set인 경우 list로 변경해서 리턴한다.

  2. return JSONEncoder.default(self, obj)
    : 객체가 set이 아닌 경우는 본래 JSONEncoder 클래스의 default 메소드를 호출해서 리턴하면 된다.

  3. app.json_encoder = CustomJSONEncoder
    : CustomJSONEncoder 클래스를 Flask의 디폴트 JSON 엔코더로 지정해 준다.
  • 이로써 jsonify 함수가 호출될 때마다 JSONEncoder가 아닌 CustomJSONEncoder 클래스가 사용된다.

위의 코드를 추가하고 시스템을 재시작시킨 후 "follow" 엔드포인트를 호출하면 정상적으로 HTTP 응답이 오게 된다.


unfollow 엔드포인트

이제 언팔로우 엔드포인트를 구현해본다.

@app.route('/unfollow', methods=['POST'])
def unfollow():
    payload           = request.json
    user_id           = int(payload['id'])
    user_id_to_follow = int(payload['unfollow'])

    if user_id not in app.users or user_id_to_follow not in app.users:
        return '유저가 존재 하지 않습니다', 400

    user = app.users[user_id]
    user.setdefault('follow', set()).discard(user_id_to_follow)

    return jsonify(user)
  1. user_id_to_follow = int(payload['unfollow'])
    : 언팔로우할 사용자 아이디를 HTTP 요청으로 전송된 데이터에서 읽어 들인다.

  2. if user_id not in app.users or user_id_to_follow not in app.users:
    : 팔로우 엔드포인트와 마찬가지로 해당 사용자 아이디 혹은 언팔로우할 사용자 아이디가 존재하지 않으면 400 Bad Request 응답을 보낸다.

  3. user.setdefault('follow', set()).discard(user_id_to_follow)
    : 언팔로우하고자 하는 사용자 아이디를 set에서 삭제한다.

  • remove 메소드가 아닌 discard 메소드를 사용하는 이유는, remove의 경우 없는 값을 삭제하려고 하면 오류가 발생되지만 discard 메소드는 삭제하고자 하는 값이 있으면 삭제를 하고 없으면 무시하게 된다.
    따라서 삭제하고자 하는 사용자 아이디가 실제로 set에 존재하는지 확인하는 로직을 구현할 필요가 없게 된다.


___

Reference

0개의 댓글