본문 바로가기

프로젝트 4주차(데모데이) KPT : 최선의 선빵 - 지옥의 CORS

프로젝트 4주차(데모데이) KPT : 최선의 선빵 - 지옥의 CORS

들어가며

 

<전체 아키텍처>

 

프론트

  • withCredentials: true 적용된 상태

 

백엔드 

https://zerohip.co.kr/ 혹은 https://www.zerohip.co.kr/ 에 접속하면 vercel uri를 프록시하고, 해당 uri에서 api.zerohip.co.kr/request uri로 요청을 보내면 해당 uri따라서 http://localhost:8080으로 프록시해서 웹 서버 내부의 로직 수행.

 


 

지옥의 CORS

 

좌절, 희망 그리고.. 좌절

산 넘어 산이라더니.

지독했던 배포 문제가 해결되고 나서, 숨 돌릴 틈도 없이 바로 CORS 이슈를 맞닥뜨리게 되었다.

  • CORS 개념을 배울 때까지만 해도, 웹 개발을 하면서 한 번쯤은 맞닥뜨린다는 CORS 에러가 나를 이렇게 극단적으로까지 괴롭게 할 것이라곤 상상도 하지 못했다.
  • 이상하게도 내가 만든 페이지는 OAuth 말고는 CORS에러가 뜨지 않았고, 나머지 프론트 팀원 분들은 모든 페이지에서 CORS 에러가 뜨는 상태였다. 특히 E님의 페이지는 진입하는 페이지부터 CORS 이슈가 있어서, E님은 그야말로 정말 아무것도 할 수 없는 상태였다.

 

이번에는 모두가 한 마음으로 바로 멘토님께 도움을 요청을 드렸고 백엔드, 프론트엔드 멘토님까지 다 붙어서 이것저것 시도를 해가며 문제 해결을 하려고 해도 도무지 원인을 알 수가 없었다.

  • 아무리 생각해도 우리 쪽 문제는 아닌 것 같았지만 새벽 2시까지 백엔드 멘토님께서 java 코드를 검토해주셨고 그 결과 백엔드 코드에는 아무 이상이 없다고 하셨고 프론트 쪽에서 검토가 필요할 것 같다는 이야기를 해주셨다.
  • 프론트엔드에서의 원인이 있나? withCredentials:true만 넣으면 된댔는데…

 

그래도 혹시나… 혹시나….

프론트엔드에서 문제가 생겼다면, 우리가 생각하는 원인은 아래와 같았다.

  1. svg 파일을 import 해오는 방식이 다르다.
  2. withCredentials 속성이 제대로 전달이 안 되었다…!
  3. 외부에서 가져오는 url, 라이브러리에서 CORS 에러가 난다. (카카오 CORS 에러 해결 참고)

 

그렇게 의심 되는 사항들은 하나 하나 짚어 가보며, 해결을 해나갔고 어떻게든 희망을 부여잡고 push를 했지만 돌아오는 것은 여전한 CORS 문구였다.

 

데모데이는 27일이었다.

 

그리고, CORS 에러는 배포 후, 24일부터 발생하기 시작했고, 그렇게

 

24일..

 

25일..

 

 

26일…

 

 

 

모두가 뜬눈으로 새벽까지 깨어 있었고, 우리는 그렇게 거대한 CORS의 벽에서 무력함을, 희망을, 좌절을 함께 했다.

 

구글, stackoverflow, 페어 프로그래밍에서 만난 분, 아고라 스테이츠 등… 모든 방법을 총동원했지만 여전히 돌아오는 답은 없었다. 그 당시, 유일한 대안처럼 보였던 아예 레포지토리를 프론트/ 백 분리해서 로직을 싹 통일시키고 코드를 재정비해볼까? 라는 생각도 해봤지만, 만약 코드에서의 문제가 없다면 시간 낭비일 뿐이었다. (지나고 보니 정말 그렇다.)

 

특히 E님께서 25일 저녁에 일찍(그래봤자 저녁 8시..ㅋㅋㅋ) 들어가보겠다고 했을 때 너무 마음이 아팠다..

누구보다도 기다리셨고, 기대하셨고, 좌절하셨기에 그 마음을 쉬이 헤아릴 수 없었다. 조용히 그저 푹 쉬고 오시라는 말밖에 할 수 없었다.

 

또 다시 희망

그래서 다시 한 번 마음을 부여잡고, 내 페이지(CORS 에러가 없음)와 다른 분들의 페이지(CORS 에러가 있음)의 차이점이 뭔지, 그리고 그게 유의미한 지표인지 하나하나 구글링을 하며 찾아다녔다.

  • 그러다 뭔가, 이상한 점을 발견했다.
  • 제일 처음에 뜨는 정적 파일에서 CORS 에러가 나는 페이지는 Response Headers에 뭐가 되게 많이 있는 것이었다.
  • 특히 바로 아래의 스샷에서 첫번째 첨부 이미지를 보면, 일단 Response Headers가 눈에 띄게 다르고, 특히 X-Matched-Path라는 항목이 CORS 에러가 나는 페이지에는 추가가 되어 있다.
  • 찾아보니 X-Matched-Path는 nginx에서 사용되는 헤더였다…!

 

 

다시 말해, 이유는 잘 모르겠지만 내가 만든 페이지는 nginx의 헤더가 없었고, 다른 분들이 만드신 페이지는 nginx의 헤더가 있었다.

  • 문제는 nginx의 헤더에 Access-Control-Allow-Origin: * 이 추가되어 있었고, 이것은 프론트에서의 withCredentials: true 와 공존할 수 없는 속성이었다. (아래 발췌 블로그 링크)

 

일단 실낱같은 희망을 다시 잡았으니, nginx 쪽을 해결해야 한다…!

  • 하지만, 문제는 nginx의 헤더 설정은 되어 있지 않았고 spring security에서 CORS 관련 설정을 이미 다 해두셨던 것이다.
    • nginx 보다 spring security에서 설정할 수 있는 항목이 더 많기 때문! 
    • (아래 내용 포함, 사실 백엔드 쪽에서 결과를 전달받기만 하여 제대로 된 과정을 설명하기는 어렵지만, 듣고 또는 이해한 바를 그대로 옮겨보았다.)
    • 아직까지도 의문인게, 왜 내 페이지에서는 nginx가 리다이렉션을 안 시켰을까? 무슨 기준이지? 가 궁금하다…
  • 그래서 임의로 spring security의 설정을 주석처리하고 nginx에서 설정을 하면 이번에는 no header CORS 에러가 났다.. 
  • 두 군데 다 하면 이번에는 multiple value CORS 에러가 났다.
    • 이번에는 CORS 에러가 나는 페이지에서는 301 **Permanently moved** (참고 링크) 응답이 왔다.

 

 

 

한 마디로 어떻게 하든 CORS 에러를 만나게 된다는 것이다…!!!!! ㅋㅋㅋㅋㅋ 😂

 

 

우선 해결에 대한 실마리를 어느정도 잡았으니

(1) nginx 담당이셨던 J님께서 nginx 설정 등 다른 곳을 점검하고 계실 때,

(2) H님께서 혹시 모르는 상황을 대비해서 동시에 spring boot 자체에 ssl을 적용하는 방법을 시도해보신다고 하셨다.

 

우선 H님 방식으로 하게되면 프론트에서도 기존에 refreshToken을 쿠키로 주고 받는 방식에서 accessToken과 동일하게 header로 주고받는 방식으로 변경이 되기 때문에 나도 동시에 코드를 고치고 있었다.

  • 그 방식으로 된다는 보장은 없었지만, 그래도 우리는 모든 가능성을 열어두고 시도해보아야만 했다.

.

.

.

.

.

.

데모데이 당일, 7시간 전

드디어 모두가 기다리던 순간이 왔다…!!! 서버가 켜졌다.

 

 

하지만, 원래부터 잘 되던 회원가입부터 막히기 시작했고 다시 한 번 우리는 기다릴 수밖에 없었다.

  • 뭔가 도와주고 싶은데… 그저 기다리면서 프론트 쪽 세팅에 대해 여쭤볼 때 바로 대답해주는 것밖에 할 수 있는 게 없었다.
  • 참으로 무력했다.

 

.

.

.

.

데모데이 당일, 5시간 전

그리고, 다시 서버가 켜졌다.

그리고

 

그리고…

 

 

 

 

우리가 그토록 보고 싶었던 화면이 브라우저에 그려지기 시작했다.

CORS 에러를 해결한 것이다..!!!!

 

됐다.

 

이때부터 도파민이 쫙 퍼지면서 몽롱했던 정신이 확 들기 시작했다.

 

 

 

그렇게, 우리는 정말 기적적으로 데모데이 날 서비스를 개시할 수 있었다.

  • 가벼운 마음으로 데모데이를 마치고, 메인 프로젝트 회고를 위해 줌에서 모인 팀원들은 (특히 H님) 졸음을 참느라 온갖 인상을 다 쓰고 있는 상태였다. ㅠㅠㅠㅠ
  • 디스코드에서 누구 하나 방을 옮겨가면 바로 뒤따라 한 분씩 한 분씩 자연스럽게 들어오시며 결국 한 방에는 늘 6명이 가득 차 있었다.
  • 브랜치 문제, 배포 이슈, CORS 에러가 났을 때 모두가 함께 디스코드에서 잠을 이겨내며 같은 시간, 같은 공간 속에서 서로를 북돋아 주며 결국에는 문제를 해결해냈다.

 

잘 하지 못해도 된다. 나도 그렇고 우리 모두는 배우러 온 사람들이니까.

 

하고자 하는 마음. 팀원 모집 때도 가장 중요하게 생각한 포인트였다.

 

처음 팀 빌딩 시간에 마지막에 두 분이 우리 팀에 합류하고 싶다는 의사를 내비치셨는데, 나는 내가 누구를 뽑을지 선택할 자격은 없다고 생각했다. 그래서 그때도 이렇게 말했다. '하고자 하는 의지와 소통이 가장 중요하기 때문에, 하겠다! 라고 먼저 말씀하시는 분과 함께 하고 싶다.' (그렇게 J님이 들어오셨다. ㅋㅋㅋ)

 

어떻게든 하고야 말겠다는 마음만 있으면 된다.

그리고 그 마음은 우리 모두가 가지고 있었다.

정말 감사하고 존경스럽고 대단한 팀원들이었다.

 

그렇게, 우리는 서로에게 최선의 선빵이었다.

 

되돌아보며

 

메인 프로젝트를 하면서 결코 쉬웠던 하루가 없었다. 부트캠프에서 임했던 그 어느 때보다도 더 밀도깊은 하루 하루를 보냈다.

 

힘들었던 것

  • 촉박한 기간 내에 달성해야 하는 목표로 인해 평정심을 잡기가 쉽지 않았다.
    • 일정관리, 브랜치, 배포, CORS… 자세한 이야기는 주차별 회고에 적혀있기에 생략하겠다.
    • 조급함을 느낄 때마다 차분하게 문제 해결을 하고, 코드를 짜려고 많은 노력을 했다.
    • 하지만 어쩔 수 없이 평정심을 살짝 잃고 집중을 못할 때도 있었다.
    • 그런 의미에서 정말 좋은 경험이었다.
    • 압박적인 상황 속에서 24시 크런치 모드로 해야 하는 일을 집중해서 업무에 몰입하여 최대의 생산성을 내는 것은 꼭 필요하다.

 

나의 K

  • 그래도 해냈다.
    • 포기하지 않았다. 지치지 않았다.
    • 수많은 이슈를 맞닥뜨리며 포기할 수 있었지만, 그래도 결국 해냈다.
    • 문제는 부딪히다 보면 언젠가는 해결은 된다. 어떤 방식이든간에.
    • 어떻게든 문제 해결을 위해 다양한 제안을 시도했다.
    • 우리는 엔지니어다. 중요한 것은 문제를 해결하는 것이다.
  • 많은 것을 배웠다.
    • 커뮤니케이션, 프로젝트 기초 세팅, 디자인 시스템, 새로운 기술 스택 등 많은 것을 배웠다.
    • 그리고 많은 것을 배우고 싶어졌다.
      • 특히 백엔드 쪽의 지식이 현재는 20% 정도 있는데, 50%까지 올리고 싶다.

 

나의 P

  • 융통성이 과했다.
    • 뭔가, 융통성은 나를 한 마디로 표현할 수 있는 말인 것 같다.
      • 교사로 재직했을 때도, 뭔가 새로운 시도를 많이 했고 아이들에게 많은 자율성을 주었다.
        • (코로나 시기에는 각자 집에서 메타버스에 접속해서 한국사 역할극을 열고,
        • 학교 마스코트 콘테스트를 열어 각 학년별 캐릭터를 만들고,
        • 학년 별 캐릭터로 키링 굿즈를 만들었다....ㅋㅋㅋㅋ)
      • 우리 반을 담당하는 전담 선생님들께서는 ‘5학년 1반은 선생님 없어도 자기들끼리 잘 돌아간다’라는 피드백을 주셨다.
    • 하지만 팀 프로젝트에서는 중심을 잡고, 가능한 검증된 레퍼런스 위주로 진행해야할 필요가 있다.
  • 비슷한 이야기지만, 어떨 때는 조급한 마음에 괴랄한(?) 문제해결방식까지 도입하고자 시도했다.(ㅋㅋㅋㅋ)
    • 나는 좀 일단 뭐~든지 시도해보고 해결되면? 땡큐!라면 E님은 신중한 원리원칙주의자셨다.
    • 따라서, 종종 브레이크 걸어주신 E님께 감사드리며 부딪히면서도 많은 것을 배울 수 있었다!
    • (E라고 쓰고 근본뿌리라고 읽는다)
  • 압박 속에서 벌어지는 문제 상황에서 의연하게 대처하는 자세 또한 필요하다.
    • 우리에게 주어진 날은 하루, 그리고 막 배포가 된 상황.
    • 그럼에도 불구하고 로컬 테스트를 하고 pr을 날린 후 merge를 했어야 했다.
    • 하지만 급한 마음에 바로바로 dev에서 바로바로 코드를 날리며 테스트를 했다.
    • 급할 수록 돌아가라 - 꼭 기억해야 할 원칙이다.

 

나의 T

  1. 검증된 레퍼런스를 먼저 찾고, 이것저것 시도해보는 것도 좋지만 결과에 대해 신중하게 검토해보기
  2. 전반적인 리팩토링하기
  3. Postman, 로컬테스트 철저하게 하기
  4. Next.js에 대한 다양한 기능 써보기 (ISR, 서버리스 등)
  5. 사용자 정의 명세서 100% 달성하기
  6. 얇고 길게 백엔드 강의 시간날 때 듣기

 

” 프로젝트 소감

  • 멘토 님께서 멘토링 1주차 때 경험하기 쉽지 않은 팀 프로젝트인 만큼 많은 것을 얻을 수 있을 것이라고 하셨다.
    • 그리고 버전 1 프로젝트가 마무리된 지금, 그게 무슨 의미인지 알게 되었다.
    • 하나의 목표로 진심으로 프로젝트에 몰두할 수 있는 환경은 그 어디에서든 쉽게 경험할 수 없다.
    • 만약, 그저 인터넷에서 사이드 프로젝트 팀원을 구하거나, 또는 친구들끼리 프로젝트를 시작한다면 이 정도의 진심과 시너지가 나오기는 쉽지 않을 것이다.
  • 팀 프로젝트를 하면서 내가 가장 자신있게 이거 하나만은 얻었다! 라고 할 수 있는 것은 바로 선빵의 정신이다.
    • 수 많은 이슈와 에러들이 있었지만 우리는 불화나 갈등이 없었다. (그쵸? 읍읍읍- 하시는 거 아니죠? ㅋㅋㅋㅋ)
    • 그렇다고 하고 싶은 이야기를 꾹 참고 분위기에 맞춘 것도 아니었다.
    • 나를 비롯해 그저 하고 싶은 말과 의견을 최선을 다해 개진했고, 그로인해 최선의 결과를 얻었다.
    • 특히, 에러를 잡기 위해 프론트엔드인 내가 백엔드 팀원 분들께 이것저것 말도 안 되는 요청을 했을 수도 있다. (거의 100%다.. ㅋㅋㅋㅋㅋ )
    • 하지만 진지하게 받아들여주셨고 제 생각엔 의미 없을 것 같은데 그래도 해볼게요 (ㅋㅋㅋㅋ)라고 말씀해주셔서 너무너무 감사했다.
  • 서로를 위한 피드백 역시 아낌없이 해 주었다. 그 사람을 진심으로 생각하는 애정을 담아서.
    • 나 역시 팀 안에서의 나는 이런 장단점이 있구나- 를 알 수 있었다.
    • 그렇게 받은 피드백은 정말 소중하다.
  • 에러가 났을 때 회의방에 누가 들어가 있으면, 어느 새 한 명씩 한 명씩 들어와 결국 6명이 한 방에 있곤했다.
    • 우리 모두가 꺾이지 않은 마음으로 서로를 지탱했다.

 


 

다시 시작

 

제로힙 프로젝트는 끝나지 않았다.

오히려 지금부터 시작이다.

 

토요일에는 우리 프로젝트를 어떻게 쭉 - 진행시킬 것인가에 대한 회의도 잡아놨다.
(물론 버전 2부터는 원하는 사람들만 진행 예정이며 contributor 방식도 고려 중이다.)

 

 

제로힙은 나의 첫 포트폴리오이긴 하지만 다르게 보면 나의 첫 작품이기도 하다.

 

이제 막 하얀 도화지 위에 스케치가 완성된 나의 작품인 것이다.

비록 23년 7월 만큼 내 일상을 던져가며 온전히 프로젝트에 몰입할 수는 없겠지만

 

 

선을 따고, 채색을 하고 배경을 채워서 완전한 작품이 될 때까지,

즉 우리가 처음 기획했던 정의 명세서가 100%가 될 때까지, (또 모른다. 130%가 될 수도)

실제로 내가 정말 쓰고 싶은 ‘진짜’ 가계부 서비스가 될 때까지,

우리의 슬로건인 당신의 과소비가 0에 수렴할 때까지,

 

제로힙은 끝나지 않는다.

728x90
⬆︎