개발 slecs

파트너 포털에 공지사항 상세 모달과 Ajax 로딩 추가

목차

partner-portal 영역에 공지사항 상세 모달과 Ajax 동적 로딩을 붙이는 작업이었다. 변경 파일은 뷰/스타일 1개. 겉으로 보면 간단해 보이지만 늘 그렇듯이 한 레이어만 건드리고 끝나는 작업은 없었다.

왜 모달 + Ajax였나

공지사항 목록은 이미 있었다. 없던 건 상세 내용을 그 자리에서 바로 보여주는 흐름이었다. 페이지 이동으로 구현할 수도 있었지만 파트너 포털 특성상 목록-상세 간 컨텍스트 유지가 더 자연스럽고, 뒤로가기/브라우저 히스토리 오염도 피할 수 있어서 모달을 선택했다.

Ajax 로딩을 함께 넣은 이유는 간단하다. 모달을 열 때마다 전체 페이지를 다시 그리면 사용자 입장에서 어색하고, 미리 전체 데이터를 내려받는 건 목록이 늘어날수록 초기 로딩에 부담이 생긴다. 그래서 클릭 시점에 해당 공지 ID를 가지고 API를 찌르는 구조로 갔다.

구현 구조

기본 흐름은 목록 행 클릭 → Ajax 호출 → 응답 데이터를 모달에 바인딩 → 표시. JSP + jQuery 기반이라 크게 복잡하진 않다.

// 공지사항 행 클릭 이벤트
$(document).on('click', '.notice-row', function () {
    var noticeId = $(this).data('id');
    $.ajax({
        url: '/partner/notice/detail',
        data: { noticeId: noticeId },
        success: function (res) {
            $('#modalTitle').text(res.title);
            $('#modalContent').html(res.content);
            $('#noticeModal').modal('show');
        },
        error: function () {
            alert('공지사항을 불러오는 데 실패했습니다.');
        }
    });
});

백엔드 엔드포인트는 /partner/notice/detail로 신규 추가. 권한 인터셉터를 통과해야 하므로 파트너 포털 공통 인터셉터 설정에 해당 URL 패턴이 포함되어 있는지 확인하는 게 첫 번째 체크포인트였다. 이걸 빠뜨리면 로그인 없이 직접 URL 접근이 가능해지거나, 반대로 정상 요청인데 403이 떨어지거나 둘 중 하나다.

사이드바 메뉴 활성화도 같이 처리했다. 공지사항 메뉴가 현재 활성 상태임을 표시하는 클래스 처리인데, 기존 뷰에서 requestURI를 비교하는 방식이었다. 신규 상세 API 경로가 생기면서 패턴 매칭이 깨질 수 있어서 확인 후 조정했음.

설계할 때 고려한 것들

단순 모달이라도 체크해야 할 지점이 생각보다 많다.

항목 내용
빈 데이터 처리 공지 내용이 null/빈 문자열일 때 모달이 비어 보이지 않도록 fallback 텍스트 처리
권한 없는 접근 인터셉터 외에 서비스 레이어에서도 파트너 권한 확인
중복 클릭 방어 빠르게 연속 클릭 시 Ajax 중복 발송 방지 - 요청 중 플래그 또는 버튼 비활성화
XSS 서버에서 내려오는 content를 .html()로 바인딩할 때 서버 측 이스케이프 여부 확인
쿼리 성능 상세 조회는 PK 단건이라 실행 계획 별도 확인 필요 없었지만, 목록 쿼리와 함께 인덱스 확인

실시간 갱신 여부는 초반에 잠깐 고민했다. 공지사항 특성상 관리자가 올리고 파트너가 보는 방향성이 명확해서 폴링이나 웹소켓을 넣을 이유가 없었고, 그냥 클릭 시 최신 데이터 요청으로 충분했다.

NULL 처리는 도메인을 가리지 않고 방어해야 한다. 특히 파트너 포털처럼 다수 파트너가 동시에 접속하는 환경에서, 특정 파트너에게만 특정 공지가 안 보이거나 모달이 깨지는 현상은 재현하기 어려운 버그가 된다. 그래서 서버 응답에 필드 하나라도 null이 섞일 수 있는 구조라면 프론트에서도 명시적으로 체크하는 게 맞다.

구현 후 직접 화면에서 동작 확인. 기존 목록 데이터가 그대로 유지되는지, 모달을 열고 닫았을 때 상태가 초기화되는지, 권한 인터셉터를 통과하는지 순서대로 돌아봤다.


사내 서비스를 만들다 보면 기능 하나가 단순히 화면에 버튼 하나 추가하는 것으로 끝나지 않는다는 걸 계속 체감한다. SQL 조회, 예외 처리, 화면 렌더링, 권한 체크가 전부 엮여 있어서 어느 하나만 빠뜨려도 특정 사용자에게 이상한 화면이 나타난다.

이번엔 비교적 단순한 케이스였지만 그래도 변경 전 동작을 먼저 메모해두고, 수정 후 같은 케이스로 확인하는 루틴을 지켰다. 관련 화면이 있으면 숫자 cross-check, 커밋 메시지는 "무엇을"보다 "왜"를 담으려고 노력. 이 루틴이 귀찮게 느껴질 때도 있는데, 나중에 문제 생겼을 때 히스토리 찾는 시간을 줄여주는 건 확실하다.

작은 단위로 커밋을 쪼개는 것도 계속 유지 중. 이번에도 백엔드 엔드포인트, 프론트 모달, 사이드바 처리를 각각 분리해서 넣었다. 한 번에 몰아서 커밋하면 나중에 어느 변경이 문제였는지 찾기가 훨씬 힘들어진다.

댓글 0

첫 댓글 달아줘.