Next.js SSR 동시성(race-condition) 장애 회고

황주현·2022년 10월 17일
18

NextJS

목록 보기
4/4
post-thumbnail

들어가며

지금 근무하고 있는 회사에서 기존 웹 서비스를 새롭게 리뉴얼하는 프로젝트를 진행했다.

기존 서비스는 빠른 속도로 우선 런칭을 하기 위해 express+jquery (일명 4드론 전략)을 사용해서 개발해 그 확장성과 유지보수성이 굉장히 떨어졌기 때문이다.

때문에 리뉴얼 프로젝트를 진행하기로 결정했고, 새로운 조합은 Spring + Next.js 였다.

원래는 react로 가려 했으나, 아직은 불안정한 SEO 이슈 해결 및 빠른 초기 렌더링 속도를 위해 Next.js로 결정하였다.



문제 발생

서비스런칭

긴 프로젝트 기간을 거쳐 드디어 리뉴얼 끝! 런칭을 진행했다.
Next.js를 사용한 회사 서비스는 처음으로 만들어본 것이었기 때문에 감개무량 + 걱정 반 + 기대 반 인 상황이었다.

그래서 아주 조마조마 하면서 배포를 시작했다.


오픈~ 생각보다 별 일이 없네

당연히 이런말을 하면 안됐다.

오픈하고 한 5분정도 까지는 문제가 없었는데, 사람들이 조금 더 접속하며 갑자기 문의가 들어오기 시작했다.


제가 원했던 데이터가 아닌 다른 데이터가 보여요 😦

다른 데이터가 자꾸 보이는데 어떻게 된건가요?? 🤨


(큰일났다)


100% 잘된다는 보장은 당연히 없기에 (특히 개발업계는 더더욱) 문제가 발생하지 않을거라고는 생각하지 않았다.

하지만 지연 정도의 이슈를 생각했지, 문의 내용의 증상은 예상하지 못했었다.



문제 인식, 파악

Don't cry spilled over milk. 😢

이미 엎질러진 물, 돌이킬 수 없기 때문에 곧바로 동료 백엔드 개발자와 문제가 무엇인지 파악부터 진행했다.

일단 문의가 들어온 내용들을 토대로 백엔드 부분의 이슈인지 프론트 엔드의 이슈인지 부터 확인했다.

증상등을 기반해 확인해 본 결과 프론트엔드 Next.jsSSR 부분에서 동시성문제(race-condition)이 발생했던 것이었다.

이제까지 React.jsCSR 환경에서만 개발했다 보니 Next.jsSSRclient가 아닌 Node.js에서 실행된다는 것을 간과해서 벌어진 일이었다.



문제 해결

(ㅠㅠ)

그나마 다행이었던 점은 초기 사용자 수가 많지 않았고, 약 3분 정도의 시간으로 어떤 부분이 문제인지 까지 파악이 됐던 것이다.

그렇지만 실제 운영중인 서비스는 3분도 짧은 시간이 아니기에 최대한 빨리 서비스를 정상화 하기 위해 문제가 되는 코드들을 확인하고 패치를 진행했다.



문제는 CSR과 SSR이 같은 방식의 코드를 공유한 것

Cookie 값을 가져와 사용하는 로직들이 있었는데, Cookie를 인자값으로 받게 하는게 아닌, 라이브러리를 통해 Cookie 값을 가져오는 식으로 개발했다. (react-cookie 같은)

개발 당시에는 CSR 환경에서 문제가 없었지만, SSR은 쿠키 값이 없기 때문에 같은 로직이 돌아가지 않았다.

그래서 이를 해결하고자 SSR 시작 시점에서 ContextCookie를 추출해 라이브러리를 통해 저장하고, SSR이 끝나는 시점에 다시 Cookie 값을 업데이트 해서 브라우저로 전달했다.

문제는 바로 여기서 발생했다.

SSR은 사실 Node.js에서 구동되기 때문에 하나의 유저가 프로세스를 타고 있을 때 다른 유저가 프로세스를 타게되면, 라이브러리로 어딘가에 저장된 Cookie 값을 덮어씌우거나 다른 값을 가져오게 됐던 것이다.

SSR의 로직 및 데이터들의 사용은 모두 Request-bound 안에서 진행이 되었어야 했는데, 그 부분을 생각하지 못해서 발생한 이슈였다.

결국 SSRCSR 로직을 분리해서 SSR의 로직들은 모두 인자값을 직접 받도록 고쳐 배포를 진행하고 문제를 해결했다.



문제 해결 후

정말 정신없이 문제를 해결하고, 왜 이런 일이 생기게 되었는지 정리해보았다.

앞서 작성했듯이 SSR을 이용한 회사의 서비스 개발은 처음이었다.
때문에 어떤식으로 코드를 작성해야 하는지 능숙하지 않았고, SSR 코드를 작성하는 부분에서 잘못된 판단을 했다.


SSR, CSR 뭐가 다른데?

예를 들어 axios 라이브러리로 쿠키값을 포함한 API 요청을 한다고 생각해보자.
CSR 환경과 달리 SSR에서는 axioswithCredential 옵션을 주더라도 정상적으로 동작하지 않는다.

왜냐하면 Node.js는 쿠키 값이 없기 때문이다.
따라서 직접 요청에 cookie 값을 주입해주는 방법을 사용해야 한다.

이런식의 CSRSSR의 차이점이 발생되는 경우가 많았는데, 그럼 결국 CSR용 코드 따로, SSR용 코드 따로 만들어야 하는건가? 하는 고민이 계속해서 쌓이게 되었다.

결론적으로 개발 당시 내가 판단한 것은 "같은 코드를 사용하자" 였다.


왜 같은 코드를 사용하자는 결론을 냈는가?

프론트엔드 개발을 하며 가장 많이 보였던 키워드 중 하나는 재사용 이었다.
중복되는 코드를 최대한 방지하고, 재사용하고, 최적화하는게 프론트엔드가 해야할 일이라고 생각했다.

때문에 당시 생각으로 CSR용 따로 SSR용 따로 만드는 건 기능은 동일한 두개의 코드를 각각 만드는 것과 다름없게 느껴졌고, 잘못 만드는 코드라고 생각했다.

그래서 최대한 그 둘의 중복되는 코드들을 하나로 사용하도록 개발했다.


간과한 점

앞서 말했듯이, SSR 구간은 Browser(Client)가 아닌 Node.js(server) 에서 실행되는 코드였고, 그 말인 즉슨 Frontend처럼'만' 생각해서는 안되는 것이었다.

만약 React.js만 사용했었다면 문제 없을 상황이었지만, Next.js는 프론트엔드이더라도 Server의 형태를 띄고 있기에 그 부분에서 생길 수 있는 이슈를 고민해봐야 했다.


문제를 해결한 후에 동료 백엔드 개발자분과 이야기를 나누며 그 부분을 좀 더 확실히 인지했다.

"백엔드에서 어떤 값을 입력받을 경우, DTO를 만들어 레이어 별로 직접 전달하도록 설계해요."
"이를 통해 각 계층 간의 의존관계를 안전하게 유지하고 동시성 문제와 같은 이슈를 피할 수 있어요."

(사실 컴포넌트도 무조건 모든걸 재사용 하는 것이 아닌, 상황에 따라 추후 개발 방향을 보고 같은 모양이라도 다른 파일로 만들기 때문에 재사용에만 초점을 줬던 것은 시야가 좁았던 확실한 판단 미스 였다.)



결론

(다행히 이런 일은 일어나지 않았다....)

Next.js를 그저 SEO에 좀 더 최적화 된 React.js 정도로만 생각했던 안일함에 문제가 있었다.

SSRCSR의 장점을 모두 갖는 것의 이면에는, '잘' 사용하기 더 어렵다는 것을 생각하지 못했던 것에 아쉬움이 남아 있다.

이번 일을 계기로 Next.js를 사용하는 것이 간단한 것이 아니라는 것을 알게 되었고, 새로운 기술을 접근하는 데에 조심하고 잘 알아봐야 한다는 것을 더더욱 깨닫게 되었다.



정리

새로운 기술을 도입할 때는 단순히 그것의 좋은 면만 바라보는 것이 아니라, 그에 따라오는 더 신경써야 하는 점 등을 고려하는 것이 중요하다.

Next.js를 그저 SEO, SSR 처리기 정도로 생각했던 사람들이 있다면, 이 글을 보고 조금 더 진지하게 임해 나 같은 일이 없도록 바란다. (ㅠㅠ)

나중에 기회가 된다면, Next.js를 서비스에 사용하는 회사들이 어떤식으로 코드를 작성하는지 알아보고 싶다.


+ 읽어주셔서 감사합니다.
+ 오타, 내용 지적, 피드백을 환영합니다. 많이 해주실 수록 제 성장의 밑거름이 됩니다.
profile
반갑습니다. 프론트엔드 개발자 황주현 입니다. 🤗

4개의 댓글

comment-user-thumbnail
2022년 10월 21일

저도 이거때문에 고생많이 했었습니다... 화이팅하세요!

1개의 답글
comment-user-thumbnail
2022년 10월 22일

고생하셨습니다 좋은 글 공유 감사합니다:>

1개의 답글