ES5 패턴을 ES6+로 — 운영 중단 없이 옮긴 방법
JSP·jQuery 전산 화면에서 ES6+로 단계 이전한 뒤, React SPA를 iframe으로 붙여 확장해 나간 경험이다.
“ES6로 바꿉시다”는 말은 회의실에서는 쉬웠다.
실제로 손대는 곳은 JSP 안쪽이었다.
JSP 전산 화면이었던 것
- 상단·좌측 메뉴는 공통
include로 묶여 있었다. - 본문 JSP에 조회 폼, 테이블, hidden 필드가 먼저 있고, 하단에
<script>가 이어졌다. - JS 파일이 분리돼 있어도, 최종 진입점은 JSP였다. 어떤 변수는
c:forEach, 어떤 값은 스크립틀릿으로 이미 박혀 나왔다. - jQuery로 이벤트를 걸고, AJAX로 전산 API를 때리는 구조가 반복됐다.
그래서 “프론트 프로젝트를 갈아엎자”보다, JSP 한 장씩 읽히게 만드는 일부터 시작했다.
JSP 안에서 먼저 한 일
- 스크립트 위치 정리: 인라인
<script>와 외부.js역할을 나눴다. JSP에는 “데이터 주입”만, 로직은 파일로. - 전역 오염 줄이기: IIFE로 감싸고, 화면별 네임스페이스(
window.ScreenXxx)를 고정했다. - ES5 → ES6+는 화면 단위: 한 JSP를 고칠 때마다 그 화면이 쓰는 JS만 손댔다.
한 번에 빌드 체계를 넣지 않았다. 운영 중인 JSP 배포 리듬에 맞추는 게 우선이었다.
우선 바꿨던 패턴 (JSP에 붙는 JS 기준)
| ES5 / 레거시 | ES6+ | JSP·운영 관점 |
|---|---|---|
var | const / let | 스크립틀릿과 섞인 스코프 버그 감소 |
function() {} 콜백 | 화살표 함수 (this 필요 시 유지) | 이벤트 핸들러 짧게 |
.then 체인 | async/await | AJAX 에러 처리 한곳으로 |
| 응답 필드 나열 | 구조 분해 | 서버 JSON 매핑 단순화 |
문자열 + | 템플릿 리터릴 | 로그·알림 메시지 정리 |
한 배포에 한 패턴이었다. JSP diff와 JS diff를 같이 리뷰받기 쉬웠다.
JSP에서 특히 조심한 것
- 서버가 내려준 값 (
${...}, hidden input)은 건드리지 않고, 읽기만 명확히 했다. document.ready안에서 DOM을 다시 찾는 순서를 바꾸지 않았다.- 공통 include 순서가 바뀌면 다른 화면이 깨질 수 있어서, include 체인은 마지막에 건드렸다.
“모던해 보이게”보다 JSP + JS 합쳐서 동일 동작이 기준이었다.
검증 (전산 운영 기준)
- 같은 조건으로 조회·저장·승인 흐름을 다시 탔다.
- 금액·날짜·권한 체크가 바뀌지 않았는지 담당자와 같이 봤다.
- 네트워크 탭에서 요청 URL·파라미터 개수가 이전과 같은지 확인했다.
- 운영 시간대를 피하고, 문제 시 이전 JS 파일로 되돌릴 경로를 남겼다.
그다음: React SPA를 iframe으로 붙였다
ES6+로 읽기 쉬운 JS를 쌓아 두니, 이 화면만 새 UI로라는 요구가 들어왔다.
당시 선택은 기존 JSP는 그대로 두고, React SPA를 iframe으로 삽입하는 방식이었다.
왜 iframe이었는가
- JSP 레이아웃·메뉴·세션은 그대로 두고, 바뀌는 영역만 격리할 수 있었다.
- React 쪽 배포와 JSP 배포를 분리할 수 있었다.
- 문제가 나면 iframe
src만 이전 버전으로 돌리거나, 빈 JSP 블록으로 막을 수 있었다. - 팀이 익숙한 전산 URL·권한 체계를 유지한 채, 신규 UI만 실험할 수 있었다.
JSP 쪽에서 한 일 (대략)
<%-- 기존 조회 영역은 유지, 신규 영역만 분리 --%>
<div id="legacy-summary">...</div>
<div id="spa-mount" style="min-height: 480px;">
<iframe
id="ledger-spa"
src="/spa/ledger-app/index.html"
title="차세대 원장"
style="width:100%; border:0;"
></iframe>
</div>
<script>
(function () {
var frame = document.getElementById('ledger-spa');
// 초기 파라미터: hidden / data-attribute 에서 읽어 postMessage 로 전달
frame.addEventListener('load', function () {
frame.contentWindow.postMessage(
{ type: 'INIT', shopId: document.getElementById('shopId').value },
window.location.origin
);
});
})();
</script>
- JSP가 초기 컨텍스트(매장, 기준일, 권한 플래그) 를 넘기고
- iframe 안 React가 조회·상세·수정 UI를 담당하게 나눴다.
- 저장·승인처럼 민감한 건 처음엔 기존 JSP API를 그대로 호출하게 두었다.
확장해 나간 순서
- 조회 전용 한 메뉴만 iframe으로 교체
postMessage로 탭 전환·리로드·에러 토스트 연동- iframe 높이
resize(내용 길이에 맞춤) - 점진적으로 등록·수정까지 SPA로 이동 (API는 Spring Boot 쪽으로 이관)
- 익숙해지면서 iframe 없이 라우트 단위 전환을 검토 (아직 진행 중인 화면도 있음)
한 번에 SPA로 갈아타지 않았다. JSP 위에 얹고 → 영역을 넓히는 순서였다.
지금 돌아보면 (당시 결론)
- ES6+ 정리는 “문법 업그레이드”가 아니라, JSP 전산을 다음 단계로 넘길 준비였다.
- iframe React는 임시 땜질이 아니라, 운영을 끊지 않는 이관 통로에 가까웠다.
- 레거시는 분석부터에서 적었던 읽기 순서가 없었으면, iframe 경계도 못 잡았을 것이다.
새 기술(React)을 빨리 쓰고 싶은 마음과, 운영 전산을 멈출 수 없다는 현실. 그 사이에서 내가 택한 답은 덮어쓰기가 아니라 얹고 넓히기였다. 한 번에 갈아엎는 멋진 그림보다, 매 배포마다 되돌릴 수 있는 작은 전진이 결국 더 멀리 갔다.
솔직히 아쉬운 것 (개선점)
- iframe 방식은 상태 공유·SEO·스크롤/포커스 처리에서 한계가 분명했다.
postMessage규약이 늘어날수록 관리 비용도 같이 늘었다. - ES6+로 정리한 화면과 아직 ES5인 화면이 공존해, 한동안 두 스타일을 동시에 유지보수해야 했다.
- 검증을 “담당자와 같이 보기”에 많이 의존했다. 자동 회귀 테스트가 약해, 사람이 빠지면 검증 품질도 흔들렸다.
앞으로 나가야 할 방향
- iframe으로 격리했던 화면을 라우트 단위 통합(같은 SPA 내 라우팅) 으로 단계적으로 끌어올린다. 이미 일부는 진행 중이다.
- 저장·승인 같은 민감 흐름의 API를 Spring Boot 쪽으로 완전 이관하고, JSP는 점차 “데이터 주입”에서도 손을 뗀다.
- 핵심 흐름(조회·저장·승인)에 E2E 회귀 테스트를 붙여, 이관할 때마다 사람이 일일이 확인하지 않아도 되게 만든다.
- 남은 ES5 화면의 이관 우선순위를 변경 빈도·장애 이력 기준으로 매겨, 감이 아니라 데이터로 순서를 정한다.
JSP 안에서 버텼던 시간이 있어서, React를 덮어쓰기가 아니라 확장으로 붙일 수 있었다. 다음 단계는 그 “확장”의 임시 통로(iframe)를 걷어내고, 정식 구조로 수렴시키는 일이다.