AI가 프로덕션 Next.js 앱에서 여전히 놓치고 있는 것들
요약
AI가 작성한 코드가 개별 컴포넌트 수준에서는 완벽해 보일지라도, 모노레포 구조, 환경 변수 보안, API 경로 설정 등 프로젝트 전체 맥락에서는 오류를 범할 수 있음을 경고합니다. Next.js 개발 시 AI가 놓치기 쉬운 인프라 및 설정 측면의 실질적인 문제점과 해결책을 다룹니다.
핵심 포인트
- 모노레포 환경에서 Turbopack 설정을 통해 Next.js 인덱싱 성능 최적화 필요
- 보안을 위해 서버 전용 환경 변수와 클라이언트용 NEXT_PUBLIC_ 변수 엄격히 분리
- AI가 제안하는 코드의 로직뿐만 아니라 프로젝트 전체의 설정 및 환경 맥락 검증 필수
- API 엔드포인트 경로 중복 및 환경별 URL 설정 오류 주의
서론 (Introduction)
당신은 AI에게 fetch를 수정해달라고 요청합니다. AI는 해냅니다. 타입(Types)도 통과합니다. 컴포넌트도 렌더링됩니다. 당신은 푸시(push)합니다.
그런 다음 개발 환경이 느려지며 멈춰버립니다. 혹은 프로덕션(prod)에서 빈 페이지가 서빙됩니다. 아니면 건드린 적도 없는 파일에서 GitHub이 충돌(conflict)을 알립니다. 모델은 파일 내부에서는 제 역할을 다했습니다. 하지만 그 주변의 리포지토리(repo)는 보지 못했습니다.
그 간극이 바로 대부분의 Next.js 고통이 실제로 존재하는 곳입니다. 깨진 JSX 때문이 아닙니다. 잘못된 루트 폴더(root folder), 잘못된 환경 계층(env layer), 잘못된 배포 모델(deploy model), 혹은 원격지의 오래된 git 참조(git ref) 때문입니다.
이 리스트는 AI 리팩터링(refactors), 통과된 CI, 그리고 깔끔한 로컬 머지(local merges) 속에서도 살아남는 문제들을 다룹니”.
1. 전체 모노레포(monorepo)를 감시하는 Next dev
문제점 (The problem)
당신의 앱은 더 큰 리포지토리 안에 위치합니다. 그 위에는 또 다른 락파일(lockfile)이 있습니다. next dev를 실행하면 여러 개의 락파일에 대한 경고가 뜨지만, 당신은 그냥 무시하고 계속 진행합니다.
컴파일(Compile)이 느려집니다. CPU 점유율이 높게 유지됩니다. 때로는 명확한 이유 없이 컴퓨터가 멈춘 것처럼 느껴집니다. AI는 더 빠른 임포트(imports)나 더 작은 컴포넌트(components)를 제안할 것입니다. 하지만 Next.js가 잘못된 트리를 인덱싱(indexing)하고 있다는 사실은 알아차리지 못할 것입니다.
해결책 (The fix)
next.config.ts에서 Turbopack을 앱 폴더로 고정(Pin)하세요:
import path from "node:path";
import { fileURLToPath } from "node:url";
...
- 이는 특정 모노레포(monorepo)/워크스페이스(workspace) 설정에 적용됩니다.
- 모든 느린 Next.js 개발 환경에 대한 해결책은 아닙니다.
만약 해당 락파일 경고가 나타난다면, 그것을 소음이 아닌 버그로 취급하십시오.
2. 서버 비밀값(secrets)을 NEXT_PUBLIC_*에 넣는 것
문제점 (The problem)
AI는 API URL과 토큰을 NEXT_PUBLIC_*에 넣습니다. 왜냐하면 클라이언트 fetch 예제들이 그렇게 사용하기 때문입니다. 해당 값들은 브라우저 번들(browser bundle)에 포함되어 배포됩니다. 누구나 빌드된 JS에서 이를 읽을 수 있습니다.
또는 그 반대의 경우도 있습니다. 서버 라우트 핸들러(route handlers)에 업스트림 URL(upstream URL)이 필요한데, 이것이 다른 곳을 가리키는 퍼블릭 변수(public var)로만 존재할 때가 있습니다. 로컬 개발(Local dev)은 편법으로 작동하지만, 프로덕션(Prod)에서는 작동하지 않습니다.
해결책 (The fix)
런타임(runtime)에 따라 분리하십시오:
- 브라우저 코드 →
NEXT_PUBLIC_*만 사용 - 라우트 핸들러(Route handlers) / 서버 코드 → 서버 환경 변수 (
NEXT_PUBLIC_접두사 없음)
업스트림 URL과 서비스 토큰은 서버에 유지되어야 합니다. 클라이언트는 당신의 BFF 또는 퍼블릭 API 서피스(public API surface)를 호출합니다. 하나의 환경 변수를 통해 두 가지를 모두 처리하지 마십시오.
3. API 주소 누락 또는 /api/v1 중복 문제
문제점
한 백엔드는 https://api.example.com으로의 호출을 기대합니다. 다른 하나는 https://api.example.com/api/v1을 기대합니다. 코드가 기본 주소 위에 경로를 추가할 때 문제가 발생합니다. 세그먼트가 하나만 잘못되어도 모든 요청이 404 오류를 반환하게 됩니다.
이는 종종 동일한 리포지토리의 다른 앱에서 .env.local을 복사했을 때 발생합니다. AI는 fetch 코드는 올바르게 복사하지만, 기본 주소가 이 앱과 일치하는지는 확인하지 않습니다.
해결 방법
해당 앱의 .env.example 파일을 열어 URL이 정확히 어떻게 구성되는지 확인하십시오. 그것에 맞추어야 합니다. 한 회사 내 모든 앱이 동일한 주소 형식을 사용한다고 가정해서는 안 됩니다.
4. 정적 HTML로 배포하는 앱에 서버 API 파일 추가
문제점
일부 Next 앱은 출력(output)을 `
AI는 프록시 설정 (proxy config)을 추가합니다. 하지만 당신이 어떻게 배포하는지에 대해서는 거의 묻지 않습니다.
해결 방법
개발(dev)과 프로덕션(prod) 환경에 서로 다른 설정을 사용하세요:
- 개발(Dev): 원한다면
next.config에 프록시 (proxy) 설정 - 프로덕션(Prod): 환경 변수 (env)에 전체 백엔드 URL을 입력하고, 백엔드에서
CORS활성화
완료되었다고 판단하기 전에, CI(지속적 통합)가 수행하는 것과 동일한 방식으로 next build를 실행하세요. next dev만으로는 충분하지 않습니다.
6. 클라이언트 컴포넌트 (client component)에 임포트된 서버 전용 코드
문제점
Next.js는 서버 코드와 클라이언트 코드 사이에 명확한 경계가 있습니다. "use client"로 표시된 파일은 브라우저에서 실행됩니다. 요청 헤더 (request headers)를 읽거나 비밀 환경 변수 (secret env vars)를 사용하는 등의 작업은 서버에서만 작동합니다.
종종 하나의 공유된 lib/api.ts 파일이 모든 곳에서 임포트(import)되곤 합니다. AI는 중복된 파일들을 병합하면서 실수로 서버 코드를 클라이언트 컴포넌트 안에 집어넣습니다. 이 경우 빌드가 실패하거나 이상한 런타임 에러 (runtime errors)가 발생합니다.
해결 방법
두 개의 파일을 유지하세요:
- 브라우저용 파일 (단순한 fetch, 공개 환경 변수만 사용)
- 서버용 파일 (비밀 환경 변수 사용,
app/api라우트 또는 서버 컴포넌트에서만 사용)
쿠키 (cookies), 헤더 (headers), 또는 프라이빗 환경 변수 (private env)를 읽는다면, 절대로 "use client" 파일에 포함되어서는 안 됩니다.
7. 로그인 토큰이 유효하지만 잘못된 앱의 토큰인 경우
문제점
일부 앱은 두 가지를 확인합니다: 사용자가 로그인했는지, 그리고 사용자의 역할 (role)이 이 특정 제품과 일치하는지입니다. .env.local에 로그인 토큰을 붙여넣었습니다. 만료되지도 않았습니다. 그런데도 앱은 여전히 당신을 로그인 페이지로 되돌려 보냅니다.
해당 토큰이 다른 대시보드나 역할에 속해 있기 때문입니다. AI는 리다이렉트 (redirect) 로직을 수정하려고 시도합니다. 체크 로직은 정상 작동하고 있습니다. 당신의 토큰이 잘못된 앱을 위한 것일 뿐입니다.
해결 방법
세 가지 사항이 일치하는지 확인하세요: 환경 변수 (env)의 역할 설정, 토큰 내부의 역할, 그리고 앱의 인증 가드 (auth guard)가 기대하는 값입니다. 로컬에서 테스트할 때는 반드시 이 앱을 위해 특별히 생성된 토큰을 사용하세요.
8. 실제 인증 (auth)을 배포하면서 가짜 로그인 기능이 여전히 켜져 있는 경우
문제점
많은 팀이 UI를 더 빠르게 구축하기 위해 실제 로그인을 건너뛰는 개발 전용 플래그 (dev-only flag)를 추가합니다. 유용하지만, 잊어버리기 쉽습니다.
당신은 인증 (auth)이 완료되었다고 말합니다. 하지만 콜백 (callback), 토큰 갱신 (token refresh), 로그아웃 (sign out), 그리고 역할 기반 리다이렉트 (role redirect)는 실제 로그인으로 테스트된 적이 없습니다. AI는 상태 (state)에 있는 사용자 객체를 보고 인증이 작동한다고 가정합니다.
해결 방법
UI 작업에만 skip-login 플래그를 사용하세요. 머지 (merge)하기 전에: 플래그를 끄고, 실제로 로그인한 뒤, 보호된 페이지를 열고, 로그아웃을 해보세요.
9. 이전 개발 서버가 여전히 포트를 점유 중인 경우
문제점
이전 프로세스를 종료하지 않고 pnpm dev를 다시 실행합니다. 3000 포트가 사용 중입니다. Next.js는 3001에서 시작됩니다. 당신은 계속 3000 포트를 새로고침하며 잘못된 앱을 보거나 아무런 유용한 정보도 보지 못하게 됩니다.
AI는 당신의 React 코드를 디버깅할 것입니다. 하지만 어떤 프로세스가 포트를 점유하고 있는지는 확인하지 않습니다.
해결 방법
lsof -i :3000
이전 프로세스를 종료하세요. 새로 시작하세요. 다른 것을 디버깅하기 전에 터미널이 어떤 포트를 말하고 있는지 확인하세요.
10. 로컬에서는 충돌이 없지만, GitHub에서는 충돌이 발생하는 경우
문제점
로컬에서 최신 main 브랜치로 자신의 브랜치를 업데이트했습니다. 머지 (merge)는 깔끔해 보입니다. 하지만 GitHub의 풀 리퀘스트 (pull request)에는 여전히 충돌 (conflict)이 표시됩니다.
당신의 로컬 브랜치와 GitHub에 있는 복사본은 더 이상 동일한 히스토리 (history)가 아닙니다. GitHub은 리베이스 (rebase)된 로컬 복사본이 아니라, 원격 브랜치 (remote branch)를 main과 비교하기 때문입니다.
AI는 "main을 다시 pull 하세요"라고 말합니다. 당신은 이미 했습니다. 오직 당신의 로컬 머신에서만 말이죠.
해결 방법
로컬에서 리베이스 (rebasing)를 마친 후, GitHub을 업데이트하세요:
git push --force-with-lease origin your-branch
의도적으로 리베이스를 했거나 브랜치 히스토리를 재작성했을 때만 이 명령어를 사용하세요.
두 브랜치가 일치하는지 확인하세요:
git log origin/your-branch..HEAD
출력이 없다면 로컬과 원격이 동기화된 상태임을 의미합니다.
11. .env 파일의 로그인 토큰 만료
문제점
로컬 개발을 위해 일부 앱은 .env.local에서 로그인 토큰을 읽어 서버 측 API 호출에 첨부합니다. 토큰은 만료됩니다. 핸들러 (handlers)는 401 Unauthorized를 반환합니다. UI에는 빈 데이터나 에러 상태가 표시됩니다.
마치 API가 고장 난 것처럼 보입니다. AI는 에러 핸들링 (error handling) 코드를 다시 작성합니다. 하지만 토큰은 그저 오래되었을 뿐입니다.
해결 방법
토큰을 디코딩 (decode)하여 만료 시간을 확인하세요. 실제 로그인을 통해 새로운 토큰을 받으세요. 개발용 토큰을 만료 날짜가 있는 비밀번호처럼 취급하세요.
12. 빌드 중이 아닌, 빌드 후에 설정된 공개 환경 변수 (Public env vars)
문제점
NEXT_PUBLIC_ 값은 빌드 타임 (build time)에 JavaScript에 기록됩니다.
이미 빌드가 완료된 후 호스팅 대시보드에서 이 값들을 설정하면, 실제 운영 중인 사이트에는 여전히 이전의 값이 박혀(baked in) 있게 됩니다.
로컬 환경의 .env.local에서는 잘 작동하지만, 프로덕션 (production) 환경에서는 잘못된 API 주소나 앱 설정이 적용됩니다.
해결책
CI에서 다음 빌드가 실행되기 전에 공개 환경 변수 (public env vars)가 존재하는지 확인하세요. 일부 팀은 빌드 단계에서 .env.production 파일을 작성하기도 합니다. 호스트 UI의 런타임 (runtime) 설정만 확인하지 말고, 빌드 로그를 확인하세요.
13. 동일한 리포지토리 내의 잘못된 앱에서 환경 변수를 복사함
문제점
하나의 리포지토리 (repo) 안에 있는 두 개의 Next.js 앱은 구조가 비슷해 보입니다. 폴더 구조도 같고 사용하는 도구도 같습니다. 하지만 필요한 환경 변수는 다릅니다. 한 앱의 .env.local을 다른 앱으로 복사해 버리는 실수를 합니다.
셸 (shell)은 정상적으로 로드됩니다. 하지만 첫 번째 API 호출이나 리다이렉트 (redirect)가 실패합니다. AI는 파일이 존재하기 때문에 환경 변수가 문제가 없다고 가정합니다.
해결책
모든 앱은 각자 고유한 .env.example 파일을 가져야 합니다. 매번 그 파일로부터 시작하세요.
환경 변수 (Env)는 리포지토리 단위가 아니라 앱 단위입니다.
14. 로그인 페이지와 대시보드가 동일한 로컬 포트에서 실행됨
문제점
보통 로그인은 하나의 앱에서 담당하고, 대시보드는 다른 앱에서 담당합니다. 로컬 개발 환경에서는 이는 서로 다른 포트(예: 로그인은 3001, 대시보드는 3000)를 의미합니다.
만약 환경 변수의 두 URL이 모두 동일한 포트를 가리키고 있다면, 리다이렉트가 혼선을 빚게 됩니다. 무한 루프가 발생하거나, 잘못된 랜딩 페이지로 이동하거나, 로그아웃 후 아무런 유효한 페이지로 연결되지 않는 등의 문제가 생깁니다.
해결책
흐름을 명확히 기록하세요: 로그인 주소 → 콜백 (callback) → 대시보드 주소.
localhost 환경이라 할지라도, 분리된 앱들에 맞춰 환경 변수 (env)를 설정하세요.
15. 코드 리뷰는 통과했지만, 아무도 프로덕션 빌드를 실행하지 않음
문제점
AI가 프롬프트에 따라 컴포넌트를 수정했습니다. 타입 (Types)도 통과했고, 코드 리뷰도 좋아 보입니다. 하지만 실제 로그인과 실제 API 호출을 포함한 전체 프로덕션 빌드 (production build)를 실행해 본 사람은 아무도 없습니다.
해결책
머지 (merge) 하기 전에 다음을 확인하세요:
next dev가 경고 없이 시작되는가next build가 CI에서 사용하는 것과 동일한 설정으로 통과하는가- 실제 로그인 1회 수행 (
skip-login플래그 해제) - 실제 데이터를 반환하는 API 호출 1회 수행
결론
AI는 단일 파일 내부에서는 강력합니다. 하지만 리포지토리 레이아웃 (repo layout), 환경 설정 (env setup), 정적 vs 서버 배포 (static vs server deploy), 그리고 GitHub 브랜치가 로컬 브랜치와 일치하는지 여부 등에 대해서는 취약합니다.
이러한 요소들은 채팅창 밖에 존재합니다. 직접 확인해 보세요. 그러면 수많은 "무작위적인" Next.js 버그들이 더 이상 무작위가 아니게 될 것입니다.
Next.js 또는 AWS 프로젝트에 도움이 필요하신가요?
지난 5년 이상 동안, 저는 다양한 산업 분야의 기업들이 사용하는 프로덕션 시스템을 구축하고 유지 관리하는 것을 도와왔습니다. 만약 다음과 같은 분야에서 도움이 필요하시다면:
- Next.js 애플리케이션
- React 아키텍처 (architecture)
- AWS 배포 (deployments)
- AI 통합 (integrations)
- 성능 최적화 (performance optimization)
언제든 편하게 연락해 주세요.
LinkedIn: Femi Akinyemi
DEV: dev.to/femi_akinyemi
Portfolio: Femi Akinyemi
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기