2026-03-18 Next 마이그레이션 작업 정리
이 글은 오늘 진행한 Next 마이그레이션 작업을 나중에 다시 꺼내 보기 위한 기록이다. 공개 블로그에 올리는 글이지만, 내부 경로, 비밀 키, 인증 토큰, 운영상 민감한 값은 의도적으로 제외했다. 대신 어떤 축을 옮겼는지, 왜 그렇게 판단했는지, 어디까지 끝났는지, 무엇이 남았는지를 가능한 한 길고 촘촘하게 남긴다.
오늘의 핵심은 단순히 화면 몇 개를 옮긴 것이 아니라, 기존 Django에서 굴리던 서비스 덩어리들을 Next 기반 구조로 실제 운영 가능한 수준까지 당겨온 데 있다. 블로그, 웹툴, 댓글, 알림, 랭킹 페이지, 운영 대시보드, 티커, SEO, 이미지 업로드 최적화, 취약점 대응, 성능 병목 정리, Redis 기반 레이트리밋 초석까지 한 번에 여러 축이 움직였다.
1. 오늘 작업의 큰 흐름
오늘 작업은 크게 여덟 축으로 나눌 수 있다.
블로그 마이그레이션 보강과 글 작성 흐름 안정화
웹툴 허브와 개별 도구 페이지 이전 및 조회수 연동
백엔드 의존 웹툴 일부 이관
댓글 시스템과 알림 시스템 구현
골드박스, 영화 랭킹, 나무위키 랭킹 페이지 추가
홈, 티커, 관리자 페이지 등 공통 운영 화면 정리
취약점, 성능 병목, 레이트리밋, 스웜 배포 관련 운영 기반 보강
SEO, 구조화데이터, 추적 스크립트 누락분 재점검
중간에 디자인 시도도 있었지만, 과한 glass 스타일과 흐르는 배경을 메인과 웹툴 허브에 억지로 넣었더니 전체 톤이 깨져서 결국 안정적인 버전으로 되돌렸다. 이 판단도 기록해둘 필요가 있다. 앞으로는 디자인 실험과 운영 기본 화면을 더 엄격히 분리하는 게 맞다.
2. 블로그 쪽에서 정리된 것
블로그는 이미 어느 정도 Next 쪽으로 올라와 있었지만, 오늘은 실제 운영 기준으로 보강된 항목이 많았다. 먼저 글 저장과 마크다운 에디터 처리 흐름을 다듬었고, 썸네일 업로드 및 본문 이미지 업로드 경험도 손봤다. Django 시절처럼 이미지를 대충 올리는 구조가 아니라, 업로드 전에 브라우저에서 리사이즈와 압축을 시도하고 결과가 더 커지면 원본을 유지하는 방향으로 잡았다.
특히 썸네일 드래그 앤 드롭이 안 되던 문제를 해결했고, 에디터 내부에서도 이미지 버튼 업로드뿐 아니라 드롭과 붙여넣기까지 같은 최적화 흐름을 타게 만들었다. 즉 블로그 작성자는 이제 클릭 업로드만이 아니라, 이미지를 에디터 안으로 바로 끌어다 놓거나 클립보드에서 붙여넣어도 업로드가 동작한다.
또 하나 중요한 변화는 블로그 포스트 ID 발급 방식이다. 예전의 현재 최대 ID를 읽어서 하나 더하는 단순 방식은 데이터가 커질수록 비싸고, 동시 작성이 생기면 경합이 난다. 그래서 오늘은 카운터 컬렉션을 기반으로 다음 ID를 원자적으로 발급하는 흐름으로 바꿨다. 지금은 대규모 CMS는 아니지만, 이건 초기에 손봐둘수록 나중에 덜 아프다.
블로그 목록 페이지도 자잘한 최적화가 들어갔다. 카테고리 이름을 렌더할 때마다 배열을 선형 탐색하던 방식은 별로 효율적이지 않았다. 그래서 카테고리 맵을 한 번 만든 뒤 O(1) 조회로 바꿨다. 규모가 작을 때는 티가 안 나지만, 목록 페이지는 자주 열리는 화면이라 사소한 낭비도 쌓이면 체감이 달라진다.
3. 웹툴 마이그레이션 진행 상황
오늘 기준으로 웹툴은 1차 마이그레이션 성공이라고 봐도 괜찮은 수준까지 올라왔다. 단순 프론트 도구들은 대부분 이전했고, 일부 서버 의존 도구도 Next Route Handler 기반으로 붙였다. 중요한 점은 이제 웹툴이 단순 장난감 묶음이 아니라, 조회수, 정렬, 공통 SEO 구조, JSON-LD, 관련 도구 내부링크, 향후 광고 배치까지 생각하는 묶음으로 바뀌었다는 점이다.
프론트형 웹툴로는 JSON 포매터, Base64 변환기, URL 인코더와 디코더, 서브넷 계산기, 한영타 변환기, 글자수 세기, 마크다운 렌더러, 퍼플렉시티 답변 정리기, 이미지 클리너, 시간 계산기, 부가세 계산기 등을 정리했다. 서버형에 가까운 쪽으로는 URL 단축기, 네이버 블로그 조회수 분석기, 검색노출 확인, 내 IP 확인 등을 붙였다. BPM 분석기는 원래 파일 분석까지 갖고 있었지만 실제 사용성과 우선순위를 따져 수동 탭 측정 전용으로 정리했다.
이 과정에서 중요한 교훈도 있었다. 처음엔 기능만 되면 된다고 생각하기 쉬운데, 실제로는 SEO용 설명층과 페이지 구조가 상당히 중요했다. 예전 Django 템플릿에는 collapse, 활용 사례, 팁, 상세 가이드 같은 블록이 많이 들어 있었고, 그게 그냥 장식이 아니라 검색 유입과 체류시간을 위한 층이었다. 그래서 이후부터는 웹툴 페이지를 만들 때 기능, 설명, 구조화데이터, 관련 도구를 하나의 세트로 보는 기준을 세웠다.
4. 웹툴 허브에서 바뀐 것
웹툴 허브도 단순 카드 나열 페이지가 아니라, 실제 탐색 허브가 되도록 손봤다. Mongo에 저장된 조회수를 읽어오고, 추천 도구 탭에서는 조회수 기준으로 다시 정렬되게 했다. 카드 제목 자체를 링크로 열 수 있게 하고, 조회수 배지는 눈에 잘 들어오도록 붉은 계열로 조정했다. AI 도구와 자체 웹툴의 표현도 분리했다. 웹툴에는 아이콘과 이모지를 붙여 빠르게 훑기 좋게 하고, AI 도구는 과하게 장난스러운 느낌을 피하기 위해 텍스트 중심으로 정리했다.
URL 단축기는 특히 서비스화 가능성을 염두에 두고 설계했다. 단순히 짧은 주소만 만드는 도구가 아니라, 나중에 링크 이동 제어, 광고 삽입, 통계, 유료화까지 고려할 수 있는 씨앗으로 보고 구조를 짰다. 공개 영역에서는 최근 생성된 링크 전체를 노출하는 대신 자주 단축되는 도메인 보드처럼 보여주는 방식으로 정리했다. 관리자에게만 상세 목록과 활성 및 비활성 토글이 보이도록 한 것도 이 흐름의 일부다.
5. 댓글 시스템과 알림 시스템
오늘 작업에서 체감상 의미가 큰 것 중 하나는 댓글 시스템이다. 이제 블로그 상세에서 댓글 작성, 답글 작성, 좋아요와 싫어요, 본인 댓글 수정과 삭제까지 Next에서 돌아간다. 더 중요한 건 댓글 알림 시스템의 뼈대를 같이 만들었다는 점이다.
방식은 전형적인 댓글 알림 시스템이다. 누군가 내 글에 댓글을 달면 글 작성자에게 알림 이벤트를 남기고, 누군가 내 댓글에 답글을 달면 답글 대상 사용자에게 알림 이벤트를 남긴다. 네브바에는 읽지 않은 댓글 알림을 보여주는 드롭다운을 달았고, 알림을 누르면 해당 글의 해당 댓글 위치로 이동하도록 연결했다. 아직 알림 전용 페이지는 만들지 않았지만, 현재 단계에서는 드롭다운만으로도 충분하다고 판단했다.
여기에 더해 댓글 관련 컬렉션에는 인덱스를 보강했다. 댓글, 댓글 투표, 알림 컬렉션이 데이터가 쌓여도 바로 무너지지 않도록 기본 인덱스를 잡아둔 것이다. 지금 서비스 규모에서 체감 성능이 크게 문제되진 않더라도, 이런 부분은 늦게 잡을수록 운영 중에 찔끔찔끔 아프다.
6. 랭킹 페이지 추가
골드박스, 영화 랭킹, 나무위키 랭킹은 생각보다 무겁지 않은 축이라 빠르게 옮겼다. 특히 골드박스는 단순 조회형 정렬 페이지라 Mongo 컬렉션을 읽고 정렬하는 정도로 충분했다. 영화 랭킹과 나무위키 랭킹도 최신 랭킹 문서를 가져와 순위표 페이지로 보여주고, 서로를 내부 링크로 연결했다.
여기서 흥미로운 고민은 티커로 갈지, 순위표 페이지로 갈지였다. 결론은 페이지 본문은 순위표가 맞고, 티커는 사이트 공통 보조 장치로 두는 게 맞다는 쪽이었다. 그래서 메인 공통 티커는 다시 살리되, 영화, 나무위키, 골드박스 개별 페이지는 순위표로 유지했다. 골드박스 티커 노출 기준도 처음엔 판매율이었지만, 이후 할인율 높은 순으로 바꿨다.
7. 메인 화면과 공통 영역에서 한 일
메인 화면은 한때 랜딩 페이지처럼 설명 블록을 잔뜩 넣는 방향으로 흔들렸지만, 지금 서비스 성격에는 그게 별로 맞지 않는다고 판단했다. 그래서 현재 메인은 인기글, 최신글, 자주 쓰는 도구를 바로 보여주는 구조로 정리했다. 홈에 들어왔을 때 뭘 클릭해야 하는지 고민하게 만들기보다, 바로 읽거나 바로 쓰게 하는 쪽이 맞다고 봤다.
네브바도 중간에 여러 번 다듬었다. 웹툴즈 메뉴는 단일 링크로 정리했고, 모바일 햄버거 메뉴의 자동 닫힘 동작도 손봤다. 관리자 메뉴에서는 Mongo Explorer 같은 위험한 항목을 제거했고, 관리자 화면 자체도 테스트용 페이지가 아니라 블로그 글 수, 댓글 수, 읽지 않은 댓글 알림, 단축 링크 수 등을 보여주는 운영 대시보드로 바꿨다.
또한 공통 레이아웃에서 Django에 있었는데 Next에서 빠졌던 추적과 검증 요소들도 다시 챙겼다. AdSense 메타와 스크립트는 이미 있었지만, Google Tag Manager, GA4, Microsoft Clarity, Google과 Naver 사이트 인증 메타, IndexNow 관련 메타를 재점검해 layout 쪽에 보강했다. 다만 이런 추적과 검증 값은 글에 자세히 남기지 않는다. 공개 문서 성격의 글에서 굳이 토큰성 값을 반복 노출할 이유는 없기 때문이다.
8. 오늘 손본 성능 병목
오늘은 기능 추가만 한 게 아니라 병목이 될 수 있는 구조도 몇 군데 선제적으로 손봤다. 가장 먼저 URL 단축 도메인 통계가 활성 링크 전체를 메모리에 올린 뒤 애플리케이션에서 JS Map으로 집계하던 부분을 Mongo aggregation으로 바꿨다. 데이터가 늘어날수록 이 차이는 커진다. DB가 할 일을 앱 서버가 대신하면 메모리와 GC 부담이 커지기 때문이다.
사이트맵도 마찬가지였다. 블로그 포스트를 전부 toArray()로 올려서 sitemap을 만들던 구조는 포스트 수가 늘어날수록 위험하다. 그래서 분할 sitemap 전략으로 바꿨다. 블로그 목록의 카테고리 조회도 맵 캐시로 단순화했고, 신규 블로그 글 ID 생성은 카운터 기반으로 교체했다. 이런 작업들은 화면이 화려해지는 건 아니지만, 나중에 서비스가 느려졌을 때 후회하지 않게 해주는 종류의 작업이다.
9. 보안과 운영 측면에서 한 일
운영 관점에서는 두 축이 중요했다. 하나는 의존성 취약점 정리, 다른 하나는 남발 방어와 배포 정리다. 먼저 에디터 쪽에서 사용하던 markdown 관련 의존성 경로를 따라 들어온 ReDoS 이슈를 확인했고, 운영 영향이 있는 범위의 취약 패키지를 패치 버전으로 고정했다.
또한 미들웨어 기반의 전역 안티크롤링 시도도 들어왔는데, 그건 그대로 받지 않고 문제점을 걸러냈다. 허용 크롤러를 먼저 통과시키는 구조는 User-Agent 위조에 너무 약했고, 메모리 기반 레이트리밋은 스웜 멀티 인스턴스 환경에서 전역 제한으로 믿기 어렵기 때문이다. 그래서 방향을 다시 잡아, 민감한 API만 Redis 기반으로 공용 카운터를 쓰는 쪽으로 정리했다.
지금은 Docker와 Swarm 환경에 Redis 서비스를 추가하고, URL 단축 생성, 댓글 작성, 검색노출 확인, 네이버 조회수 분석, 내 IP 확인, 웹툴 조회수 집계 같은 API에 공용 레이트리밋 유틸을 붙였다. 로그인은 현재 Supabase 흐름이라 서버 POST 라우트가 아니라서 이번 연결 대상에서는 제외했다. 이 부분은 나중에 인증 시나리오가 바뀌면 다시 볼 수 있다.
10. 스웜 배포와 운영 메모
배포 쪽에서도 정리가 있었다. 원래 이 서버에서는 공개 레포를 빌드 셔틀처럼 쓰는 구조가 있었고, 실제 스웜 반영은 별도 배포 스크립트로 처리했다. Docker Swarm에서 환경변수가 기대처럼 안 들어가던 문제가 있었기 때문에, 환경변수 누락을 덜 내는 서버용 배포 스크립트도 정리해뒀다.
중간에 스웜으로 띄운 최신 컨테이너와 과거 단독 실행 컨테이너가 서로 다른 포트를 보고 있어서 왜 과거 버전이냐는 상황도 있었는데, 결국 문제는 이미지가 아니라 어떤 포트를 보고 있느냐였다. 이런 운영 디버깅도 글로 남겨두는 게 나중에 생각보다 도움이 된다. 배포 문제는 종종 코드 문제가 아니라, 오래 떠 있는 예전 컨테이너나 프록시 설정 충돌인 경우가 많다.
11. 디자인 관련 판단도 기록
오늘 한 일 중 코드만큼 중요한 건 어떤 디자인은 안 받는다는 기준을 세운 것이다. 외부 브랜치에서 홈과 웹툴 허브에 glass 스타일과 과한 배경 그라디언트를 실험적으로 넣은 변경이 있었는데, 실제 화면에서 보니 서비스 톤과 너무 어긋났다. 특히 메인과 웹툴 허브는 정보 밀도와 가독성이 중요한 페이지라, 화려함 때문에 기본 시인성이 죽으면 오히려 역효과가 난다.
그래서 이런 실험성 스타일은 마지막 정상 커밋 상태로 되돌렸다. 이건 앞으로도 중요한 기준이다. 디자인 문서나 아이디어는 받을 수 있지만, 운영 기본 화면에 반영하는 건 더 보수적으로 가야 한다. 특히 홈, 네브바, 웹툴 허브, 관리자 페이지처럼 모든 사용자가 반복해서 보는 화면일수록 더 그렇다.
12. 오늘 남겨둘 대표 커밋 묶음
커밋 메시지를 기준으로만 봐도 오늘 한 일의 방향이 드러난다. 전부를 나열하면 너무 길지만, 대표 묶음은 아래 정도로 정리할 수 있다.
블로그 저장, 편집, 미디어 처리 안정화
웹툴즈 마이그레이션과 조회수 연동
웹툴 SEO 원칙 문서화와 JSON-LD 공통 적용
URL 단축기 MVP와 통계 보강
네이버 분석기, 검색노출 확인, 내 IP 확인 추가
디자인 가이드 정리와 이후 일부 실험분 롤백
골드박스, 영화 랭킹, 나무위키 랭킹 추가
블로그 댓글과 알림 시스템 추가
파비콘, 사이트맵, 메타, 구조화데이터 보강
성능 병목 선제 최적화
markdown-it 취약점 대응
Redis 기반 레이트리밋 추가
실제로는 커밋 수가 더 많고 세부 수정도 더 많지만, 큰 흐름을 기억하는 데는 이 정도 축으로 묶는 게 더 낫다.
13. 지금 시점의 완료와 보류 판단
오늘 기준으로 끝난 것과 보류할 것도 구분이 꽤 선명해졌다.
블로그: 핵심 기능은 운영 가능한 수준
웹툴: 1차 마이그레이션 성공
댓글과 알림: 1차 완성
랭킹 페이지: 운영 가능한 수준
관리자와 공통 운영 화면: 최소 운영 형태 확보
계정(accounts): Django 마이그레이션 필요성 거의 사라짐, Supabase 기반 정리로 충분
웹하드(webhard): 비용 대비 우선순위 낮음, 동결 또는 조회와 다운로드만 나중 검토
즉 오늘 이후 기준으로는 무조건 더 옮기기보다, 이제 운영 안정화와 품질 정리로 넘어갈 구간이라는 판단이 가능하다. 모든 걸 한 번에 옮기는 것보다, 이미 옮겨진 축을 덜 부서지게 만드는 단계가 점점 중요해지고 있다.
14. 오늘 작업에서 얻은 교훈
오늘은 기능을 옮긴 날이기도 했지만, 동시에 기준을 세운 날이기도 했다. 기능만 돌아가면 된다는 생각으로 가면 SEO가 빠지고, 운영이 흔들리고, 배포가 꼬이고, 디자인 실험이 실제 서비스 화면을 어지럽힐 수 있다. 반대로 기준만 세우고 구현이 느리면 마이그레이션 자체가 진도를 못 뺀다. 결국 중요한 건 기능, 운영, SEO, 디자인, 배포를 동시에 조금씩 진전시키는 균형이다.
그리고 JS와 TS, Next는 여전히 피곤하다. 파이썬과 Django에 비해 시각적으로도 정신없고, 빌드, 타입, 번들, 서버와 클라이언트 경계까지 한꺼번에 신경 써야 한다. 그럼에도 불구하고 지금처럼 실제 운영 구조를 Next로 옮기기 시작하면, 한 프레임 안에서 페이지와 API와 SEO를 같이 다룰 수 있다는 장점도 분명히 있다. 결국 오늘의 결론은 완전히 편하진 않지만, 지금 방향은 맞다에 가깝다.
15. 다음에 이어서 볼 것
이 글을 나중에 다시 열어본다면, 다음 순서로 이어보면 된다.
Redis가 실제 스웜에서 안정적으로 붙는지 점검
레이트리밋 대상 API를 더 넓힐지 판단
홈과 웹툴 허브 디자인 실험은 별도 브랜치에서만 검토
웹하드는 당장 마이그레이션하지 말고 동결 여부 판단
블로그 목록, 댓글, 알림 쪽 운영 로그를 보면서 실제 병목 재점검
SEO 관련 누락 메타와 검증 요소는 주기적으로 Django와 비교 점검
여기까지가 2026-03-18 기준 작업 기록이다. 다음엔 또 다른 문제가 생기겠지만, 적어도 오늘 무엇을 옮겼고 무엇을 보류했고 어떤 판단을 했는지는 이 글 하나만 보면 다시 감을 잡을 수 있게 만드는 것이 목적이다.