👉 I need YOU for the world’s leading companies!
Frontend
: JavaScript, React.js, SASS, React-router-dom, Styled ComponentBackend
: Python, Django, MySQL, AWS(EC2, RDS, S3), Docker협업 및 일정 관리
: Git, Github, Slack, Trello, Notion, Figma구현 파트 | |
---|---|
김준영 | 모델링, 소셜로그인, 채용리스트, 회사/채용 상세정보, 직군별 연봉, 월간 구독 서비스 Needed+ |
김성수 | 모델링, 직업 카테고리 리스트, 이력서(파일) 업로드 및 삭제/조회(soft-delete 적용) ,검색 기능 |
모든 것이 처음인 1차 프로젝트 때 보다 생각보다 여유롭게 진행된 프로젝트였던 것 같다.
프로젝트에 필요한 요구 기능들을 분석해보니 원티드라는 사이트가 모델링만 복잡할 뿐 1차 프로젝트때 진행했던 다른 커머스 사이트와 차이가 거의 없어서 백엔드 팀원인 성수님이랑 1주차 때 처음해보는 기능들을 최우선으로 구현하는 데에 모든 역량을 쏟기로 합의했다.
1주차 때 나는 OAuth 2.0
를 이용한 소셜 로그인 파트를 맡았고, 성수님은 AWS S3
를 이용한 이력서(파일) 업로드 기능을 구현했다.
2주차는 직군별 연봉 그래프 API를 중점적으로 개발했고, 채용정보/회사정보/채용지원 등 1차 프로젝트에서 개발했던 것과(커머스 사이트에서 상품 장바구니 추가 및 주문) 동일한 나머지 API를 작성했다,
내가 소셜 로그인을 구현하면서 막혔던 블로커는 다음과 같다.
1. 리디렉션
기존 프로젝트에서 로그인 회원가입 인증 과정은 백엔드가 작성한 API를 프론트엔드가 fetch
혹은 axios
함수를 이용해 사용하기만 하면 되기 때문에 작동 여부를 확인하는 통신을 제외하면 서로가 깊이 관여할 일이 없었지만, OAuth 2.0
은 위 그림과 같이 여러 단계의 과정을 거쳐서 인증/인가 과정을 거치기 때문에 프론트엔드/백엔드간 역할 분담과 통신이 더욱 중요해졌다.
처음에는 멘토님이 백엔드가 모든 과정을 할 수 있다고 하셔서 내가 위 그림의 첫번째 단계인 인증 코드 요청부터 마지막 단계인 응답 전달까지의 모든 과정을 담당하려고 했었고, 여러 시행 착오를 거쳐서 1주차 금요일에 사이트에서 발급한 JWT 토큰을 응답에 넣어 전달하는 것까지 성공했다. 그 과정은 다음과 같다.
1. 클라이언트가 카카오 로그인 버튼을 누르면, 카카오에서 인증 코드를 요청하는 페이지로 리다이렉트 해주는 API가 실행이 된다.
2. 리다이렉트가 성공적으로 완료되면, 클라이언트는 카카오 로그인 과정을 거치고, 로그인에 성공하면 카카오 개발자 페이지에서 내가 설정한 콜백 URI로 카카오에서 발급한 인증 코드를 쿼리스트링에 포함한 채 다시 리다이렉트 된다.
3. 콜백 URI에서는 KakaoCallbackView
을 실행한다. 발급받은 카카오 인증 코드를 request.GET.get
메서드로 변수에 저장한 뒤, 차례대로 토큰 요청 및 전달, 카카오 API 호출, 유효성 확인, 응답 전달 과정을 한번에 실행한다. 여기서 카카오 API 호출은 토큰을 요청하는 API, 로그인/회원가입 과정에 필요한 사용자 정보를 가져오는 API를 호출한다.
4. 카카오에서 유저 정보를 가져오는데 성공했다면, JWT 토큰을 발급하고 성공 메세지를 리턴한다. 만약 DB에 해당하는 카카오 유저 정보가 없다면, 가져온 데이터를 DB에 저장하고 사이트 자체 회원가입 페이지로 연결한다.
하지만 문제가 있었는데, 클라이언트가 로그인 버튼을 누르고 모든 과정에 성공한 뒤 최종적으로 클라이언트가 연결되는 페이지가 KakaoCallbackView
의 리턴값인 JsonResponse
페이지인 것이였다. 카카오 로그인에 성공하면 브라우저에서는 흰 화면에 세션 스토리지로 저장되어야 할 JWT 토큰값과 성공 메시지만 덩그러니 존재했다.
이것 또한 리턴으로 JsonResponse
가 아니라 프론트엔드에서 설정한 메인페이지로 리다이렉트를 해주면 해결되긴 하지만 점점 백엔드 API와는 멀어지는 것 같아서 멘토님, 팀원과 회의를 거쳐서 포기했다.
최종적으로는 인증 코드 요청과 인증 코드로 토큰 요청 부분은 프론트엔드 담당인 예지님이 맡기로 하였고, 백엔드에서 전부 처리한 해당 부분은 수정하게 되었다.
나는 소셜 로그인을 구현하는 것에 1주차 수,목,금 3일을 온전히 쏟아부었는데, 결과적으로 내가 짠 코드(리디렉션, 카카오 토큰 요청 API)가 사용되지는 않았지만 그래도 모든 것을 다 내 손에서 해결하려는 것에서 의미가 있었다고 생각한다. 문제를 해결하는 과정에서 OAuth
에 대해 잘 알게 된것도 좋았다.
2. API 클래스화
1차 프로젝트에서 외부 함수나 클래스 호출은 utils.py
에 인가 과정에 필요한 authorization
데코레이터를 작성해 모듈화해서 사용한 것 하나밖에 없었지만, 2차 프로젝트 소셜 로그인에서는 외부 API를 호출하는 것 까지 추가되었다.
카카오 소셜 로그인에서는 토큰 요청, 카카오 유저 정보 가져오기 2개의 API를 사용하였는데, 이것을 별개의 함수로 사용하기보다 클래스로 만들어 한꺼번에 관리해 보라는 멘토님의 과제가 있어서 클래스로 만들어 보게 되었다.
클래스를 만들어보는 과제에서 미흡했던 객체
,생성자
,초기화
,인스턴스
등 클래스에 대한 개념을 다시 한번 정립하게 되었다. 특히 기억에 남았던 점은 클래스는 데이터
와 액션
의 결합이며, 데이터에 따라서 액션이 달라지므로 데이터를 고정시키면 안된다는 멘토님의 조언이었다.
처음에 토큰 요청 API를 작성할 때 API에서 필요한 클라이언트 ID
, 리디렉션 URI
, Grant_type
등의 데이터를 클래스 안에 넣었는데, 데이터가 달라지면 클래스가 무용지물이 되므로, 코드의 유지 보수에 좋지 않았다. 따라서 API를 요청하는 사용자가 직접 입력하게 해 클래스의 원래 용도에 맞게 하드코딩
을 피했다. 나의 선입견과 고정 관념을 타파해주는 좋은 계기가 되었다.
3. CORS
프론트엔드와 역할 분담을 마치고 코드를 수정하고 최종적으로 통신하는 과정에서 맞이한 블로커다.
CORS
는 교차 출처 리소스 공유(Cross Origin Resource Sharing)의 줄임말로, 쉽게 말하면 실행 중인 API에서 다른 출처(도메인)의 자원에 접근할 수 있는 권한을 부여하도록 알려주는 체제다.
프론트엔드가 Javascript SDK로 구현한 카카오 로그인 과정은 로그인 성공 후 토큰을 백엔드 API에 넘기고, 백엔드 API는 다시 카카오 API에 토큰을 넘겨 사용자 정보를 가져오는 작업을 수행한다.
이 때 프론트엔드 클라이언트 서버는 http://localhost:3000
이고, 카카오 API에 요청을 보내는 출처는 백엔드에서 설정한 http://localhost:8000/users/signin/kakao/callback
라서 서로 출처가 달라 SOP 정책을 위반하게 된다.
프로젝트 초기 세팅에서 CORS 관련 코드를 삽입해 문제가 없을 거라 생각했는데 브라우저 콘솔에서 CORS 에러가 나서 해결하는데 꽤 애를 먹었다. 원인은 초기세팅에서 설정한 CORS를 허용하는 헤더(CORS_ALLOW_HEADERS
)에 내가 프론트엔드에서 받도록 설정한 토큰 헤더 이름access-token
이 없는 것이 문제였다.
access-token
코드를 추가하자 CORS 에러가 나오지 않아 통신이 성공적으로 이루어졌고 최종적으로 깃허브에 merge까지 마쳤다. 프로젝트 1주차 내내 나를 괴롭히던 3가지 블로커를 모두 해결하고 소셜 로그인 구현에 성공하니 감격해 살짝 눈물이 나올 뻔 했다ㅋㅋ.
직군별 연봉 그래프 API는 1주차때의 난적인 소셜 로그인을 마무리하고 여유롭게 진행한 2주차 일정에서 그나마 블로커를 느꼈던 것이다.
DB에 있는 회원들을 분류해 0년차(신입)부터 10년차 이상까지 연차별로 평균 연봉을 계산에 프론트엔드에 넘겨 주는 API인데, 원래는 Case
,When
을 활용한 조건적 Annotate
를 써서 개발하려고 했다.
코드를 짜면서 문제가 생겼는데, Annotate
를 사용하면 연차별로 평균 연봉 컬럼을 새로 만들어서 전달하는 것이므로 salary_career_0
부터 salary_career_10
까지 총 11개의 컬럼을 만들어야 하는데 새로운 컬럼을 만드는 것이므로 Dictionary comprehension을 사용해서 중복된 코드를 줄일 수 없다는 것이 문제였다.
고민 끝에 멘토님을 찾아가서 물어봤는데 생각보다 간단하게 해답이 나왔다. Annotate
를 활용하는게 아닌 리스트와 인덱스를 사용해 연차별 평균 연봉을 저장하라는 조언이었고, 알려 주시면서 자료구조를 공부해야 될 필요성에 대한 좋은 예라는 것도 알려주셨다. 리스트를 활용하라는 조언을 들으니 물꼬가 트였고 금방 코드를 짤 수 있었다. 1. 직군별로 회원을 필터링
2. 필터링한 직군에서 연차별로 계산한 평균 연봉을 0번 인덱스는 0년차의 평균 연봉으로 리스트에 저장
3. 딕셔너리 컴프리헨션과 enumerate
메서드를 이용해 2번에서 만든 리스트를 프론트엔드에 전달할 Key/Value 형식으로 변환
여태까지 API를 개발하면서 데이터를 전달할 때 어떤 자료구조가 효율적인지는 생각조차 못하고 있었는데, 직군별 연봉 API를 개발하면서 약간이나마 느끼게 되어 다시 한번 내가 성장하는 계기가 되었다고 생각한다. 👍
이번 2차 프로젝트는 1차 프로젝트때의 경험을 살려 일정 관리를 잘 지킨게 제일 좋았다고 생각한다. 계획한대로 1주차때는 처음 개발해보는 기능 2가지(소셜 로그인, 파일 업로드)를 나누어 맡아 끝내고, 2주차 때는 1차때 경험이 있던 나머지 API를 개발한다는 계획이었는데, 일정 관리에 성공하니 2차 프로젝트에서 처음 배운 개념인 Unit test
, Docker
같은 것들을 여유롭게 학습하고 프로젝트에 적용할 수 있었다.
새로운 것을 배우는 것도 중요하지만 기존에 배웠던 것을 유지하는 것도 중요한데, 이번 프로젝트를 하면서 그 2가지가 조화롭게 잘 이루어진 것 같아 좋았다.
그리고 1차때 아쉬운 점으로 남았던 프론트엔드와의 소통도 잘 이루어져 개발한 API를 서로 맞춰보는 통신에도 문제가 없었고, 이로 인해 팀워크와 팀 분위기가 매우 좋았던 것도 기억에 남는다. 프로젝트에서 느꼈던 좋았던 점을 그대로 끌고가 더 열심히 개발에 정진해야겠다. 😄