← 목록으로
안과 사이트 운영 빌드 전환과 SSR·검색 노출 개선Next.js · React · JavaScript
완료
안과 사이트 운영 빌드 전환과 SSR·검색 노출 개선
개발 서버로 돌고 초기 HTML에 스피너만 있던 안과 사이트를 프로덕션 빌드와 i18n SSR로 바꾼 작업
- 2025년 4월 안과 사이트의 Next.js·NestJS 초기 구조를 만들고, 이어서 다국어 구조와 배포를 맡았습니다.
- 오픈 때 개발 모드로 둔 프론트엔드를 프로덕션 빌드로 바꿔 설정 변경 때 나던 502를 없앴습니다.
- 다국어 Provider를 동기 초기화로 바꿔, 스피너만 있던 초기 HTML에 본문과 내부 링크가 담기게 했습니다.
문제
2025년 4월 이 안과 사이트의 Next.js·NestJS 초기 구조를 직접 만들었다. 이후 페이지 퍼블리싱, 관리자, 백엔드 기능 대부분은 동료들이 만들었고, 다국어 구조와 배포는 주로 제가 맡았다.
2025년 7월 오픈 때 프로덕션 빌드가 CSS·폰트·Swiper import 문제로 통과하지 않았다. 그래서 개발 모드로 먼저 열고 프로덕션 전환은 따로 하기로 했다. 운영 프론트엔드가 next dev로 도는 동안에는 설정 파일이 바뀌면 개발 서버가 컨테이너 안에서 재시작했고, 그때마다 nginx 연결이 끊겨 502가 났다.
다국어 Provider도 문제였다. 2025년 다국어 작업 때 번역을 클라이언트에서 비동기로 불러오고 로딩 중 스피너를 보여주도록 만들었다. 이후 서버 렌더링 분기가 임시로 막히면서, 초기 HTML에는 스피너만 남았다. 헤더, 본문 섹션, 푸터가 모두 빠졌고 H1과 내부 링크는 0개였다. 검색 엔진과 AI 크롤러가 읽을 본문이 없었다.
해결
1. 운영 프론트엔드를 프로덕션 빌드로 전환
- 운영 compose의 프론트엔드를
next build후next start로 도는 이미지로 바꿨다. - 소스와
.next볼륨 마운트를 뺐다. 마운트가 이미지에 빌드된 결과물을 가리기 때문이다. NEXT_PUBLIC_*값은 빌드 시점에 인라인되도록 정리했다.- 배포 절차, 빌드 메모리 주의점, 검증 명령을 작업 노트로 남겼다.
2. 초기 HTML에 본문이 담기게 SSR 전환
- 먼저 저위험 보완책을 올렸다. H1, 진료 설명, FAQ, 주요 내부 링크를 담은 서버 컴포넌트를 Provider 밖에서 렌더했다. 화면에는 보이지 않는 기존
.blind패턴을 썼다. - 같은 날 근본 해결을 적용했다. 번역 리소스를 정적으로 번들해 서버와 클라이언트에서 동기로 초기화하고, 스피너 게이트를 없앴다.
- SSR 중 react-i18next가 다른 React 인스턴스를 잡아
useContext에러가 나던 번들 설정을 정리했다. - 보완 블록의 숨김 H1이 모든 하위 페이지에 똑같이 들어가는 것을 확인하고 제거했다.
3. 검색·AI 크롤러 대응
robots.txt에 ChatGPT 검색 봇(OAI-SearchBot) 허용 규칙을 넣었다.- 홈
<head>에MedicalClinic·WebSite·WebPage·BreadcrumbListJSON-LD를 서버 렌더링으로 넣었다. - 상대 경로 og:image가 localhost 주소로 렌더링되던 문제를 절대 URL로 고쳤다.
- 커스텀 청크 분할 때문에 vendors CSS가
<script>로도 로드되던 문제를 청크 설정 제거로 고쳤다. - Search Console 사이트 인증을 추가했다.
4. 도메인·HTTPS 설정
- 서브도메인(사이트·관리자·API)과 Let's Encrypt HTTPS용 nginx 설정을 main에 반영했다.
- 관리자 앱도 프로덕션 빌드로 바꾸고, DB 호스트 포트 노출을 막았다.
- DNS, DB 방화벽, 인증서 발급, 업로드 파일 이전, 롤백 순서를 이전 가이드로 정리했다.
| 정한 것 | 대신 버린 것 | 이유 |
|---|---|---|
| 2025년 오픈 방식개발 모드로 먼저 오픈하고 프로덕션 빌드 전환은 따로 진행 | 프로덕션 빌드로 오픈 | 프로덕션 빌드에서 CSS·폰트 문제가 남았고 Swiper import도 바꿔야 했다. |
| 운영 프론트엔드 실행 방식next build + next start 이미지, 소스·.next 볼륨 마운트 제거 | next dev 개발 서버 + 소스 마운트 | next dev는 설정 파일 변경을 감지하면 컨테이너 안에서 재시작해 nginx 연결이 끊기며 502가 났다. 소스를 마운트하면 이미지에 빌드된 .next가 가려져 next start가 깨진다. |
| 초기 HTML 누락 1차 대응화면 변화 없는 서버 컴포넌트 SEO 블록을 Provider 밖에 렌더 | 곧바로 앱 전체 SSR 리팩터링 | 전체 SSR은 i18n 코어와 전 페이지에 영향을 주고 hydration 위험이 있어 테스트가 필요했다. 보완책을 먼저 올리고 같은 날 전체 SSR로 근본 해결했다. |
| i18n 초기화번역 리소스를 정적 번들해 서버·클라이언트에서 동기 초기화 | dynamic import로 비동기 로드하고 로딩 중 스피너 표시 | 비동기 로드는 클라이언트에서만 끝나 초기 HTML에 콘텐츠가 빠졌다. 동기 초기화로 서버와 클라이언트가 같은 결과를 렌더해 hydration이 맞는다. |
| 공통 숨김 H1layout에 들어간 숨김 h1 제거 | 보완 블록의 숨김 h1 유지 | 모든 하위 페이지에 같은 숨김 h1이 들어가 중복과 클로킹 위험이 있었다. |
결과
- 0 → 91
- 초기 HTML 내부 링크 (SSR 전환 전 → 후)
- 4개
- 초기 HTML에 서버 렌더링되는 언어 (ko·en·ja·zh)
- 운영 프론트엔드가 개발 서버 대신 프로덕션 빌드로 돈다. 설정 변경 때 나던 재시작과 502가 사라졌다.
- ko·en·ja·zh 모든 언어의 초기 HTML에 헤더, 섹션, 푸터, 내부 링크 91개가 번역된 채 들어간다.
- 크롤러용 robots 규칙, 구조화 데이터, og 이미지, CSS 중복 로드를 함께 정리했다.
- 검색 유입이나 순위 변화는 측정 기록이 없다.
- 팀 프로젝트였다. 현재 코드에서 제가 쓴 줄은 git blame 기준 약 16%다.
- 2026년 6월 작업은 Claude Code와 함께 했고, 해당 커밋에 공동 작성자로 남아 있다.
계약상 병원 실명, 도메인, 화면 캡처는 공개하지 않는다.
이 프로젝트가 궁금하시면 편하게 물어보세요.
연락하기