Python BackEnd: 클론 코딩(2)

0

BackEnd with Python

목록 보기
6/9
post-thumbnail

follow 및 unfollow 엔드포인트 구현

  • follow 혹은 unfollow하고 싶은 사용자의 id를 HTTP요청으로 전송
  • 해당 API에서 요청 처리

follow 엔드포인트에 전송할 내용?

*JSON 데이터를 전송할 것이며 다음과 같다.

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

"id" 필드는 사용자의 id이다.
"follow" 필드는 사용자가 follow할 대상자의 id이다.

unfollow 엔드포인트에 전송할 내용?

마찬가지로 *JSON 데이터를 전송할 것이며 다음과 같다.

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

"id" 필드는 사용자의 id이다.
"follow" 필드는 사용자가 unfollow할 대상자의 id이다.

위의 내용을 코드로 구성해야 한다.
'앞과 비슷하니 스스로 생각하여 구현해본 후 제시된 예제와 비교해보자'라고 적혀 있다.

구현해본다.

@app.route("/follow", methods=['POST']
def follow():
    req_follow 	    = request.json
    req_follow_id   = req_follow['id']
    req_followed_id = req_follow['follow']
    
    return jsonify(req_follow)

모르겠다. 그냥 제시된 예제를 볼 거임.

follow를 구현하는 예제 코드

@app.route('/flow', 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 'undefined user', 400
    
    user = app.users[user_id]
    user.setdefault('follow', set()).add(user_id_to_follow)
    
    return jsonify(user)

전의 포스트에서 계속 이어지기 때문에
이전에 쓰던 변수를 기억하는 것이 매우 개떡같다.

일단 내 코드가 망한 이유?

  • id가 존재하지 않는 경우를 고려하지 않음
  • 메서드 'setdefault'를 알지 못함

망하면 안 되니까 알아 가야 함.

setdefault에 대하여*

Python 내장 자료구조인 dict형을 사용하다보면
어떤 key에 대한 value가 없는 경우에 대한 처리를 해야 하는 경우가 발생한다.

key만 남고 value가 없는 dict를 set이라 하며,
dict와 마찬가지로 각 key값은 유일해야 한다.

dict.setdefault(첫 번째 인자, 두 번째 인자) 에서

  1. 첫 번째 인자: key값
  2. 두 번째 인자: 기본값(default value)

key값이 존재한다면 key값을 반환하고 없다면 기본값을 반환한다.

user.setdefault('follow', set()).add(user_id_to_follow) 에서
첫 번재 인자는 'follow'이고
두 번째 인자는 set()이다.

'follow'가 이미 있다면 'follow'반환하고 .add(user_id_to_follow) 까지 실행하여
'follow'에 user_id_to_follow를 key값으로 넣는다.

'follow'가 없다면 set():empy set과 연결하여 추가한다.

해당 사용자가 follow하는 다른 사용자들의 id를 저장하는 자료구조로 set을 사용하는 이유는?

이미 follow하고 있는 id를 또 follow하겠다는 요청이 들어왔을 때, 동일한 id가 여러번 저장되는 일이 없기 때문이다.

Python 자료구조의 dict와 set에서 각 key값은 유일하다.

아무튼 이 코드를 적당히 또 섞어줘야 함.
어디에?
app.py에

그러고나서 다시 회원가입 요청부터( /sign-up ) 다시 해주고
follow 요청을 해주겠다.

$ http -v POST http://localhost:5000/follow id=1 follow=2
이렇게 터미널에 입력해주면

OK 응답을 볼 수가 있다.

unfollow를 구현하는 예제 코드

@app.route('/unfollow', methods=['POST'])
def unfollow():
    payload		= request.json
    user_id		= int(payload['id'])
    user_id_to_follow	= int(payloadp['unfollow'])
    
    if user_id not in app.users or user_id_to_follow not in app.users;
        return 'undefined user', 400
    
    user = app.user[user_id]
    user.setdefault('follow', set()).discard(user_id_to_follow)
    
    return jsonify(user)

달라진 점은 .add대신 .discard를 쓴다는 것이다.
추가 대신 삭제를 수행해야 하기 때문이다.

.remove 대신 .discard를 쓰는 이유?
.remove 는 없는 값 지우려 하면 난리를 침. 오류가 남.
그런데 .discard는 지우려 하는 값이 존재하면 삭제하고 없으면 무시함.
상황에 맞게 더 나은 걸 쓰면 될 듯하다.

코드를 짰으니 이것도 돌려봤다.
$ http -v POST http://localhost:5000/unfollow id=1 follow=2

오타를 몇 번이나 수정한 끝에 간신히 되는 걸 확인했다.

TypeError: object of type set is not JSON serializable

만약 이런 오류가 뜬다면
원인은 Python의 json모듈이 set 자료구조를 JSON으로 변경하지 못하기 때문이므로 코드를 좀 작성해줘야 한다.

from flask.json import JSONEncoder

class CustomJSONEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, set):
            return list(obj)
        return JSONEncoder.default(self, obj)
app.json_encoder = CustomJSONEncoder

JSON 엔코더를 커스텀해서 기존의 JSON 엔코더에 덮어씌우는 과정이다.
set을 list로 변경해줌으로써 문제없이 JSON으로 변경 가능하도록 해준다.

0개의 댓글