nextjs server Action을 적극 도입하게 되었다.
02 Dec 2025 | memo nextjs server-action1우리 회사의 기존 방식은 이러했다.
msa 구조였고, 레일웨이에 서버 컨테이너들을 올리고, 게이트웨이 api를 통해 개발을 했는데, 레일웨이의 공용 config 환경을 dev로 해놨지만 레일웨이 게이트웨이 주소가 https다보니까 nestjs 백엔드 서버에서 보내는 cookie가 프론트 및 브라우저에 저장이 안됬다.
즉 레일웨이 주소는 https고 프론트는 http localhost 환경이고 도메인이 다르니 sameSite:none, secure는 false로 해줬어야 했는데 문제는, sameSite:none 이라면 secure는 무조건 true여야 브라우저가 에러를 안던진다.
그래서 기존 프로젝트의 구조를 바꿨다.
바꾼 구조 1
회사 백엔드 환경을 그냥 회사 백엔드라 칭하겠다.
- 모든 요청은 Route Handler를 경유한다.
Route Handler도 서버지만 nextjs위에서 돌아가기 때문에 모든 요청을 Route Handler를 경유하게 했다. 즉, 회사 서버 레일웨이 요청을 Router Handler가 담당하게 해줬다.
- Route Handler에서 Cookie를 설정한다.
jwt token을 요청해서 return 값으로 받고 그대로 다시 setCookies(accessToken)을 해주었다.
회사 백엔드에서 jwt token의 payload 및 유효기간을 설정해놓은 token을 그대로 return해주고있는데, 그 값을 그대로 Route Handler에서 받아서 setCookies를 해준다.
결과적으로 이렇게 Route Handler를 경유하게 해주니까 Route Handler를 오케스트라 형식으로도 쓸 수 있다는 장점이 생겼다.
바꾼 구조 1의 문제점
- f12 네트워크탭에서의 디버깅이 어려웠다.
Route Handler는 브라우저와 백엔드 사이의 독립된 서버레이어로 동작하기 때문에 네트워크 탭에 요청이 안떠서 디버깅이 힘들었다.
- Route Handler → lib/api → 프론트 및 SSR → 다시 Route Handler 로 이어지는 어떻게 보면 ‘순환’에 가까운 구조가 되어버렸다.
이렇게 된 가장 큰 배경은, 에러 처리를 프론트에서 직접 제어해야 하는 상황 때문이었다.
- 프론트에서 에러 처리를 따로해주어야했다.
Route Hanlder에서 에러 처리를 보통 다음과 같이 해준다고 치자
export async function GET(req: NextRequest) {
...
const res = await fetch('/somthingsAPI')
const data = await res.json()
if (!res.ok) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
...
}
이걸 프론트에서 호출하면 아래와 같이 호출하게 될텐데
const res = await fetch('/api/somthingRouteHandler');
라우트핸들러에서 던지는 에러를 다시 감지해줘야하는 번거로움이 있었다.
const res = await fetch('/api/somthingRouteHandler');
if (!res.ok) {
// 에러처리
}
라우트핸들러는 서버고, 프론트에서 그걸 호출하는것이기 때문에 에러 처리를 해줘야하는건 당연하지만 뭔가 아름답지못한 구조임에는 분명했다.
그래서 현 개발팀장이면서 CTO님께서 주신 의견(또는 조언..) 끝에 구조를 변경하게되었다.
바꾼 구조 2
Route Handler를 거치지 않고 Server Action을 직접 도입하는 방식으로 구조를 재정비했다. Server Action 역시 서버 환경에서 실행되기 때문에, 쿠키 처리와 같은 브라우저 제약에서 비교적 자유롭다.
물론, 이 선택이 개발 측면에서 무조건적인 이득만 있는 것은 아니다. 개발팀장님께 배운 것 중 하나는, 어떤 기술 선택에도 트레이드오프가 존재하며 그 손익을 비교했을 때 전체적으로 이득이 크다면 과감하게 선택해도 된다는 것이었다.
이번 변화로 얻은 점은 다음과 같다.
-
순환 구조에 가까웠던 요청 흐름을 완전히 끊을 수 있었다.
-
프론트에서 별도로 에러 처리를 구현할 필요가 없어졌다. (Server Action 내부에서 예외를 제어하며 보다 일관적인 흐름 유지 가능)
반면, 잃은 점도 분명히 존재한다.
-
고통스러운 리팩토링 과정 (기존 lib/api 구조와 사용 패턴을 대거 수정해야 함)
-
빠듯한 일정 속에서 추가적인 작업 부담이 발생했다.
마무리
이 문제를 덮어두고 점진적으로 리펙토링하는 방식을 따를 수 있겠지만, 현실적으로 바라봤을때 msa 구조고 그걸 호출하는 service 폴더가 연쇄적으로 묶여있다보니
지금 그나마 코드가 쌓여있지 않을 때 확실하게 잡고가는것이 낫다는 판단을했다.
앉았으니, 해보는 거죠