마인드셋

패스트캠퍼스X야놀자 프론트엔드 개발 부트캠프_FastCatch 숙박 예약 사이트 후기

furaha 2023. 12. 22. 23:13
반응형

* 프로젝트명 : FastCatch (빨리잡아!)

* 주제 : 숙박 예약 API 서비스 

* 개발기간 : 11/20(월) ~ 12/01(금)

* 깃헙주소 : https://github.com/FC-FastCatch/FastCatch-FrontEnd

* 배포사이트 : https://www.fastcatchapp.com/


 

 

이번 미니 프로젝트를 진행하면서 배운 점 그리고 아쉬운 점 맡은 기능 중 보완한 부분에 대해 작성하려고 한다.

 

1. 배운 점

 

프로젝트란 팀의 결과물이기 때문에 컨벤션이 아주 중요하다

 

이전 회사에서 했던 일이나 이전 프로젝트를 진행하면서 팀의 컨벤션에 대해 신경을 많이 쓰지 못했었다.

일하면서는 대부분 혼자 역할을 처음부터 끝까지 맡았기 때문에 컨벤션이 네이밍정도였고

그 나머지의 코드들은 내가 해오던 스타일대로 작성하기 일쑤였다. 

그런데 이번에는 프로젝트를 진행하면서 혹은 리팩토링을 하면서

사소하게는 pr의 제목부터 시작해서 코드의 통일성과 재사용성을 팀원들과 서로 같이 신경을 썼던 것 같다.

그로 인해 통일감이 있는 코드가 많지 않았나 싶다.

 

지금은 팀끼리 컨벤션을 정했지만, 회사에 들어가면 css 네이밍부터 시작해서

이미 만들어진 컨벤션과 코드문화가 굉장히 많을 텐데

그것들을 철두철미하게 지키는 것이 매우 중요하게 될 것 같다.

 

그래서 프로젝트를 거듭하면서 이 부분에 대한 훈련이 되어가는 느낌이다.

 

위 컨벤션들을 다 지키려면 뇌의 무리가 간다

 

저번에 야놀자의 프론트 팀장님이 오셔서 특강을 해주셨는데

한 분이 "팀의 컨벤션이 초반에는 잘 지켜지다가 갈수록 무너져요,,"라는 고민거리를 말씀해 주셨는데,

이에 대한 답변으로는 일제히 같은 룰을 지키게 하기 위해서는 강제하는 것이

가장 효율적이라며 북한의 정치 체제를 예시로 들어주고는 하셨었다. 

컨벤션이 중요한 만큼 사람이 아닌 기계를 통해 강제하는 것이 매우 필요하고 유용하다.

Eslint 나 배포사이트 테스트를 통해 설정과 다른 점들을 알려주는 것만큼 확실하고 편한 것은 없는 것 같다.

무엇을 강제할지에 대한 것을 논의하고 그것을 설정하면 코드를 작성하기에 굉장히 편한 환경을 만들어주는 것 같다.

그래서 프로젝트 초기설정이 그만큼 중요한 것 같아서 다음 프로젝트 때에는 

eslint 뿐 아니라 path, import order 등 처음에 규칙을 많이 잡아가고 싶다.

그리고 husky 도 도입해서 꼭 배포한 뒤에 에러를 접하는 것이 아닌 IDE의 터미널에서 바로바로 확인하고 싶다.

 

코드리뷰는 실력 향상에 좋다

 

코드리뷰의 장점을 이번 프로젝트를 통해 알게 되었다.

다른 사람의 코드 혹은 나의 코드를 보면서 왜 사용했지? 에 대한 생각을 할 수 있게 만들어주는 것 같다.

나의 코드를 설명하기 위해서는 작성한 이유와 장단점을 명확히 알고 있어야 한다.

그래서 자기 코드에 대한 설득력을 키우는데 코드리뷰가 좋은 훈련이 되는구나 싶었다.

 

또한 다른 사람의 코드에 대해 이해를 하기 위해서도 코드리뷰는 매우 좋은 역할을 하는 것 같다.

내가 맡은 역할만 알고 있는 것이 아니라, 다른 기능들도 같이 팔로업했을 때 

혹시 내가 그 기능과 연관된 것을 맡게 되거나 아니면 고민하는 부분을 같이 의논해 줄 때

그 코드에 대한 상황 캐치가 빨라서 굉장히 유용한 것 같다. 


 

2. 아쉬운 점

 

api 통신 오류를 접하면 원인 파악이 잘 안 된다

 

백엔드와의 협업이 이번 프로젝트가 처음이었다. 그래서 통신 오류를 접했을 때, 무엇이 문제인지 원인을 찾는데 많은 시간을 할애했다. cors에러, 400 에러, 401 에러, 500 에러 이 중에는 내가 api 주소를 잘못 적은 탓에 난 오류도 있었지만 프론트 측에서는 해결할 수 없는 백엔드 이슈도 같이 있어서 그 이슈를 찾기까지 시간 소비가 많았던 것 같다. 이 오류들을 앞으로도 많이 접할 텐데 네트워크 탭을 보고서도 상황파악이 될 수 있도록 네트워크 지식을 쌓는 것도 중요할 것 같다.

 

 

백엔드와의 이슈 1 

Cors 에러 : 협업할 때 제일 흔한 에러인 것 같다. Cors는 서버와 클라이언트가 정해진 헤더를 통해 서로 요청/응답할 수 있도록 액세스에 제한을 거는 보안 메커니즘이다. 특정한 리소스에만 접근 가능하도록 제한을 하기 때문에 호스트명, 포트, 프로토콜 등 서버에서 약속한 출처들을 허용해주어야 한다.

 

여기에서 Preflighted request의 개념도 처음 알았는데, 브라우저에서 진짜 요청을 보내기 전에 미리 확인 요청을 보내는 유형이다.

 

(네트워크에 관련한 것은 다음 포스팅에 더 자세히 정리를 할 것이다.)

 

백엔드와의 이슈 2

Mixed Contents 에러 : 이 문제로 거의 반나절은 시간을 많이 소비한 것 같다. 해당 에러는 https가 아닌 http연결을 통해 로드를 시도할 때 발생하는 에러이다. 프론트 측은 vercel로 배포를 했기 때문에 https에서 http로 요청을 보내는 상황이었고, 서버에서는 보안인증서를 아직 적용하기 전이었다. 그래서 서버에서 보안인증서를 연동해서 통신하게끔 해결했으나 백엔드에서도 이 부분에서 상당히 많은 시간을 할애하셨다.

 

임시방편으로 메타태그, 프록시서버 등 방법들을 제안해 주셨었다.


 

3. 내가 맡은 기능과 보완한 점

 

내가 맡은 기능 중 리팩토링 기간에 보완했던 부분에 대해 대표적으로 한 가지를 적어보고자 한다.

 

로그인 기능을 처음 구현해 봤다.

프론트측에서 고려해야 할 사항은 다음 3가지인 것 같다.

 

- api 요청 시 token 및 재발급 로직 구현

- 토큰 저장하는 방식

- 로그인 유지하는 방법

 

첫 번째는 axios interceptor를 활용하여 토큰 재발급 로직을 구현했다.

개발할 때 HTTP 요청을 많이 다루게 되는데, 이때 반복되는 패턴이나 코드 (토큰을 헤더에 포함시키거나, 공통된 에러처리 등) 들을 줄이기 위해서 interceptor를 활용했다. interceptor는 요청과 응답으로 나누어 각 과정에서 세밀하게 제어를 할 수 있다.

요청을 보낼 때에는 accessToken 유무 -> accessToken 기한 체크 -> 만료되었다면 재발급 요청해서 로그인이 유지되도록 구현하였고

응답을 받을 때에는 만약 accessToken을 갱신하는 refreshToken마저 기한이 만료되었다면 재 로그인을 유도하도록 구현했다.

 

두 번째는

처음에는 로그인 api 요청 시 발급받는 토큰이 accessToken과 refreshToken 두 가지를 발급받아 localStorage에 저장해서 사용했었다. 보안에 대해서는 고려하지 않고 값을 유지시키기 위한 방편으로 저장소를 사용했다. 그러나 localStorage나 sessionStorage 같은 경우 javascript로 읽고 쓰는 접근이 가능해서 아무나 그 메모리에 담긴 값을 불러와서 사용이 가능하기 때문에 탈취 위험이 있다고 한다.

 

이때, 웹 공격 기술에 대해서 궁금해졌다.

일단 HTTP에는 보안기능이 없다고 한다. 그러나 HTTP가 공격대상이 되는 경우는 거의 없고, HTTP를 이용하는 서버, 클라이언트, 웹의 리소스 등이 공격대상이라고 한다. HTTP 요청을 보낼 때 request 메시지는 클라이언트 쪽에서 자유롭게 변경이 가능하기 때문에 일반적으로 공격 코드를 request 메시지에 담아서 실행시킨다고 한다.

 

아래 크게 두 가지 패턴을 적어보았다.

active attack (서버를 노리는) : 웹에서 request 메시지를 통해 공격 코드를 보내는 타입 

-> 이 부분의 백 지식이 요구되는 부분이다.

passive attack (유저를 노리는) : 웹에 접근해서 직접 공격하지 않고 유저에게 공격 코드를 실행시키는 타입

-> 이 부분에 대해 더 알아보자

 

passive attack에서 대표적으로 XSS, CSRF 공격이 있다고 한다.

 

XSS(Cross-Site-Scripting) : html 태그나 js 등을 동작시켜 브라우저상에서 액션이 일어나는 공격이다.

ex) 가짜 폼을 만들어서 유저의 개인정보를 탈취한다

ex) script로 유저의 쿠키 값을 탈취하거나 유저가 의도하지 않은 request가 송신된다.

 

CSRF(Cross-Site Request Forgeries) : 인증된 유저가 의도치 않은 개인정보 등을 공격자가 설치해 둔 함정에 의해 강제적으로 정보를 바꿔버리는 공격이다.

 

내가 선택한 localStorage에 정보를 저장하는 방식을 위 두 공격에 취약한 방법임을 알게 되었다.

 

그래서 refreshToken 쿠키 요청/응답 + accessToken 로컬변수 + silent refresh 이 조합을 바꿔보려고 시도했다.

 

refreshToken은 서버 측에서부터 안전하게 다뤄져야 한다. refreshToken 만으로 accessToken을 발급 받을 수도 있기 때문이다. 그래서 HTTPS 연결을 통해서만 전송이 되도록 secure httpOnly 쿠키 저장 방식을 사용하여 쿠키에 담아 전송이 되면 XSS 공격에 대한 보호 수단 중 하나가 될 수 있다. 서버에서 전달하는 방식에 맞춰 쿠키로 요청/응답을 받으면 프론트에서는 별다른 조작이 필요없다. 브라우저가 관리를 하기 때문이다. 그래서 요청을 보낼 때에도 자동으로 정보가 보내진다는 이점이 있다. 

그래서 react-cookie 라이브러리를 이용하여 저장 수단을 바꿔보았으나 사실 백에서 애초에 쿠키로 넘겨주었다면,,,? 더욱 좋았겠다ㅜ

 

또한 accessToken을 스크립트에 직접 삽입할 수 없도록 설계하는 것이 중요하다. 그래서 저장소를 사용하지 않고 로컬 변수로 관리하면 클라이언트 측에서 직접 토큰에 접근할 수 없어서 XSS 공격을 어렵게 만든다. 또한 localStorage와 쿠키는 다른 스크립트나 도메인에서도 접근이 가능하지만, 로컬 변수는 해당 페이지의 스크립트에서만 접근이 가능하기 때문에 토큰이 악의적으로 탈취되는 위험이 줄어들 수 있다. 그리고 일부 애플리케이션의 보안 정책으로 localStorage나 쿠키에 민감한 정보를 저장하지 않도록 권고하는 경우가 있어서 이 경우에 대비해서도 보안 정책에 부합한 코드를 짤 수 있다.

--> 대신 값 유지가 되지 않기 때문에 매번 refreshToken을 이용해 재발급을 받아야 한다. 이 부분에 대한 api 가 없어서 수정하지는 않았다ㅜ (아래 3번 이어서..)

 

이렇게 쿠키에 refreshToken만 저장하고 새로운 accessToken을 받아와 인증에 이용하는 구조는 CSRF 공격을 방어할 수 있다고 한다. refreshToken으로 accessToken을 받아도 accessToken을 스크립트에 삽입할 수 없다면 accessToken을 사용해 유저 정보를 가져올 수 없기 때문이다.

 

세 번째는 로그인을 유지하는 방법이다. 

 

위 방법과 같이 하게 되면 로그인 유지가 되지 않는다. 왜냐하면 accessToken을 메모리에 저장하지 않았기 때문에 값이 유지가 되지 않는다. 그래서 이 부분은 api 가 필요하다. 새로고침을 하거나, 브라우저를 로그인한 상태에서 닫았다가 다시 열었을 때 조용히 자동 로그인을 시키는 것이다. 그러려면 refreshToken을 가지고 accessToken과 refreshToken 두 가지를 다시 재발급을 받아야 한다. 이렇게 사용자가 모르게 조용히 자동 로그인 시키는 방법을 silent refresh라고 한다.


 

결론적으로 로그인 기능을 구현하면서 느낀 부분은

보안 측면을 고려한다면 클라이언트 측에서만 커버할 수는 없는 노릇이다.

그래서 만약 작업하기 전에 백엔드 분들과 미리 어떤 방식으로 요청/응답이 될 것인지에 대해 논의를 했었다면 좋았을 것 같다고 느꼈고

 

최근에 유튜브에서 if 문 하나를 쓰지 않았다는 이유로 대학교 사이트의 개인정보들이 대거 유출된 사건을 보았다.

프론트 측에서 작성하는 코드들은 전부 오픈코드들이다. 그래서 보안에 대해서는 생각해보지 않았는데,

이번 기회를 통해서 프론트 측에서도 보안에 대해 같이 노력할 것들이 있다는 것을 알게 되었다.

흔히 사용하는 코드 중에서는 Form의 유효성 검사, 그리고 url의 쿼리 노출되지 않게 중요한 정보는 get 요청을 하지 않는 것 등등

 

다음 프로젝트에서는 백엔드와 자주 소통하며 이 부분에 대해서 신경 써봐야겠다!

(그리고 쿠키를 다시 활용하게 된다면 middleware 로 클라이언트로 들어오지 않고도 로그인 여부를 확인해보고 싶다!)

 

 

 

 

 

 

반응형