본격적으로 항해99가 시작됐다. 1주차 커리큘럼은 전체적으로 웹 개발이 어떻게 흘러가는 지 겪어보기 위해서 프로젝트를 진행하는 주차다.
따라서 1주차에는 팀별로 주제를 정해서 웹 페이지 한 개를 기획 - 개발 - 배포까지 진행한다.
팀 배정과 팀장은 랜덤으로 뽑힌다. 나는 1주차에 10조 팀장으로 뽑혔고 팀원들에게 연락을 해서 팀을 모았다.
처음에는 나 포함 4명의 팀원으로 시작했다. 그러나 본격적인 프로젝트가 시작되기 전에, 팀원 중 한 분이 개인적인 사정으로 항해99를 떠나셨다.
그리고 몇 일이 지나지 않아 다른 한 분도 항해99를 떠나셔서 남은 인원은 총 2명이 되었다.
하지만 이대로 포기할 수 는 없지! 나와 남은 팀원은 으쌰으쌰해서 프로젝트를 완성하기로 했다.
우리가 기획한 프로젝트는 직방이나 다방처럼 부동산 플랫폼에 있는 허위 매물을 공유할 수 있는 커뮤니티를 만드는 것이다.
각 부동산 플랫폼에는 부동산 매물을 쉽게 찾을 수 있지만 그 매물을 보고 온 후기나 관련 커뮤니티를 찾기가 어렵다.
따라서 부동산 후기 커뮤니티를 만들어서 자신이 보고 온 부동산 매물의 직방, 다방 등 플랫폼 url을 입력하고 사진과 후기를 남길 수 있도록 하였다.
이제 프로젝트 기획도 완료됐으니 본격적으로 설계에 들어갔다.
프로젝트 설계는 크게 3 단계로 나눠졌다. 하나는 디자인 파트, 하나는 API 파트, 마지막은 인프라 파트다.
디자인 파트는 와이어프레임 설계로 했고, 인프라 파트는 팀장인 나의 mongodb와 aws ec2를 사용하기로 했다.
그러나 API 부분에서 조금 시간이 걸렸고 그 부분에 대해서 얘기해보고자 한다.
API란?
API는 Web Application Interface의 축약어로 "해당 어플리케이션이 웹으로 제공하는 기능의 명세"라는 뜻이다.
API는 웹 기반으로 동작하기 때문에 HTTP를 주로 사용하며 반환 타입으로 xml, json, html 등이 있다.
"우리 웹 사이트에 이런 방식으로 요청하면 이런 데이터를 줄게! 를 명시한 것"
따라서 우리 프로젝트 내에서 사용할 서버의 API를 설계해야 다른 사람들과의 협업과 구현이 쉬워진다.
REST API
이런 API를 설계하기 위해서 팀 내부에서 규칙이나 컨벤션을 세워서 해도 되지만 주로 REST API라는 규칙을 따른다.
REST API는 2000년도에 로이 필딩이 정의한 HTTP 통신 아키텍처다. HTTP를 효율적으로 사용하기 위해 만들어졌고 실제로도 효율이 좋다.
하지만 REST API는 지켜야 하는 규약이 강하다. 특히 REST API에서 강조하는 6개의 특징을 모두 갖춰야 한다. (cashable, self-descriptiveness, Uniform, stateless, client-server, layerd)
이 모두를 지키는 것은 어렵기 때문에 주로 REST API의 몇 가지 규칙만 가져와서 구현하고 이를 RESTful 하다고 한다.
(실제로 로이 필딩은 이런 용어를 좋아하지 않는다고 한다.)
RESTful API를 만들기 위해선 다음과 같은 형태를 만들어야 한다.
!!METHOD + URL!!
METHOD는 HTTP의 목적, 행동에 대해 나타낸다.
METHOD는 다양한 키워드가 있지만 주로 GET, PUST, PUT, DELETE를 사용하며 각각 읽기, 쓰기, 수정하기, 삭제하기 역할를 나타낸다.
URL은 인터넷 상의 자원의 위치를 나타내는 것으로 주로 슬래시(/)로 각 계층을 구분한다.
무슨 말이냐면 아래의 URL이 있다고 하자,
http://restapi.example.com/animals/mammals/whales
그렇다면 위 URL은 restapi.example.com이라는 서버 내의 animal이라는 폴더가 있고 그 하위 폴더에는 mammals라는 폴더가 있으며 그 안에 있는 whales라는 파일을 지칭한다.
따라서 이 whales라는 파일을 읽어오기 위해서 RESTful API에 다음과 같이 요청하면 된다.
GET http://restapi.example.com/animals/mammals/whales
응답
interface는 함수라고도 불리며 input과 output이 있다. input은 위의 METHOD + URL로 볼 수 있다. 그러면 output은 어떻게 되는가
output은 주로 응답코드와 데이터로 구성된다.
응답코드는 웹 표준으로 숫자(코드)로 결과값을 나타낸다.
자주 쓰이는 코드는 200번대, 400번대, 500번대이며 요청이 성공하면 200번, 잘못된 요청이면 400번, 서버에 문제가 생기면 500번 코드를 돌려준다.
우리가 흔히 보는 404 NotFound는 쉽게 얘기하면 "너가 요청한 데이터를 찾을 수 없다! 확인해봐라!"라는 뜻이다. 즉 너가 잘못 입력해서 내가 못찾음!이다.
데이터는 내가 요청한 데이터를 돌려주는데 정해진 형식으로 돌려준다. 이 형식은 json, xml, html 등이 있는데 주로 json을 많이 사용한다.
json은 JavaScript Object Notation의 축약어로 쉽게 말하면 자바스크립트에서 쓰는 객체 표현 방식이다.
생김새는 위와 같이 생겼고 {}로 데이터 단위를 표현하고 내부에 키, 밸류로 데이터를 저장한다.
따라서 RESTful API와의 통신 과정을 정리하면 다음과 같다.
최종 API
기능 명 | Method | URL | Request | Response |
---|---|---|---|---|
로그인 기능 | POST | /signin | { “id” : string, “pw” : string } | { “result” : 0 or 1 } |
회원가입 기능 | POST | /signup | { “id” : string, “pw” : string, “nickname” : string } | { “result” : 0 or 1 } |
게시글 조회 | GET | /posts | { “address” : string } | { “posts” : [ ] } |
게시글 등록 | POST | /posts | { “address” : string, “star_point” : integer, “title” : string, “description” : string, “link” : string, ”nickname” : string } | { “result” : 0 or 1 } |
게시글 삭제 | DELETE | /posts/{id} | { “id” : integer, “nickname” : string } | { “result” : 0 or 1 } |
프로젝트 기획, 설계 모두 끝났으니 이제 구현으로 들어갔다. 이번 프로젝트에 주어진 요구사항 중 하나는 로그인을 구현할 때, JWT로 구현하라는 것이다.
JWT
JWT는 Json Web Token의 축약어라 "Json 형식으로 암호화 된 토큰"이다. 여기서 중요한 것은 '암호화'된 토큰으로 주로 로그인에서 사용된다.
구조는 크게 헤더, 페이로드, 서명으로 되어 있으며 각각 토큰의 정보, 토큰으로 전달하려는 데이터, 데이터 검증의 역할을 담당한다.
헤더와 서명은 주로 토큰의 정보와 검증 방법이기 때문에 프레임워크나 라이브러리의 도움을 받아서 쉽게 해결할 수 있다.
우리가 집중해야하는 것은 페이로드, 즉 내용이다. 페이로드는 클레임이라는 데이터 단위로 구분이 되며 키, 밸류로 구성된다.
이 클레임은 개발자가 임의의 키와 밸류로 설정할 수 있지만 이미 '이걸 사용하자라'는 약속된 클레임도 있다.
iss: 토큰 발급자 (issuer)
sub: 토큰 제목 (subject)
aud: 토큰 대상자 (audience)
exp: 토큰의 만료시간 (expiraton)
nbf: 토큰 시작시간(Not Before)
iat: 토큰이 발급된 시간 (issued at)
jti: JWT의 고유 식별자
따라서 가능하면 위 클레임에 맞게 데이터를 입력하고 꼭 필요하면 추가해서 사용하는 방식이 좋다.
JWT의 탈취!
기존 로그인 방식은 세션 / 쿠키 방식이다. 세션은 서버에서 저장하는 인메모리 데이터베이스로 이 세션을 통해서 간단한 데이터를 저장할 수 있다. 쿠키는 HTTP 메소드 헤더에 데이터를 저장하는 것으로 간단한 데이터를 저장해서 주고 받을 수 있다.
따라서 기존 로그인은 성공하면 세션에 로그인 정보를 저장하고 세션 id라는 값을 준다. 쿠키에 이 세션 id 값을 담아서 로그인을 유지 시킨다.
즉 "로그인 -> 세션 저장 -> 세션 id 쿠키 저장 -> 나중에 다시 접속 -> 서버는 쿠키 확인 -> 세션 id로 세션에 저장된 로그인 정보 가져오기 -> 로그인 유지" 순이다.
하지만 JWT를 활용한 로그인은 세션을 활용하지 않고 이 JWT에 로그인 정보를 담아서 클라이언트가 이 토큰을 저장하도록 한다. 쉽게 말해 서버가 로그인 정보를 저장하지 않고 클라이언트가 그 정보를 가지고 있도록 한다.
이렇게 하면 서버의 데이터 필요량이나 부하가 적어져서 더 좋은 성능을 만들어 낼 수 있다. 하지만 이 토큰이 탈취 당해서 잘못된 접근을 할 시, 이를 구분할 방법이 없다! 따라서 이 만료 시간 이라는 녀석을 통해 토큰이 알아서 자동으로 만료되도록 한다.
따라서 JWT의 단점인 탈취에 대해 어느 정도 대응할 수 있게 된다.
구현
이번 프로젝트에서는 exp(토큰 만료 시간)만 담아서 JWT를 만들기로 했다.
파이썬에서는 pyjwt라는 라이브러리를 통해 쉽게 JWT를 구현할 수 있고 구현한 코드는 아래와 같다.
if result is not None:
payload = {
'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24) # 로그인 24시간 유지
}
token = jwt.encode(payload, SECRET_KEY,
algorithm='HS256') # jwt는 암호화 해주는 코드: 암호화 jwt --- pyload값 id 또는 구분해주는 값으로 암호화된 값을 encording해주는 것
# .decode('utf-8')
return jsonify({'result': 'success', 'token': token})
# 찾지 못하면
else:
return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})
우여곡절이 많았지만 결국 성공적으로 프로젝트를 마무리할 수 있었다.
짧은 기간, 작은 규모의 프로젝트지만 정말 실제 프로젝트에서 겪을 만한 일을 모두 겪은 거 같다.
프로젝트 기획, 프로젝트 설계, 팀원의 이탈, 팀원 간의 의사소통, 기술적 이슈, 배포, 발표 및 문서화 등 웹 프로젝트 전반적인 모든 과정을 겪어서 자신감이 많이 올라왔다.
실제로 REST API와 JWT는 전에 공부했던 내용이지만 실제 프로젝트에서 사용해본 건 별로 없었다. 그래서 내가 공부했던 내용을 다시 한 번 정리해볼 수 있는 좋은 기회였다.
이제 다음 주차는 알고리즘 주차로 주특기에 맞는 언어를 선택해서 알고리즘을 푸는 주차다. 나는 python으로 알고리즘을 준비했는데 이번 기회에 java를 활용하여 알고리즘을 풀어볼 수 있을 것이다.(기대)
코드 및 문서 : Github : mini project - Real State
간단한 실행 영상 : 유튜브 과제 영상