diff --git a/src/main/resources/static/js/egovframework/common.js b/src/main/resources/static/js/egovframework/common.js index c9c1115..c6535bd 100644 --- a/src/main/resources/static/js/egovframework/common.js +++ b/src/main/resources/static/js/egovframework/common.js @@ -1,5 +1,3 @@ -const contextPath = $('#contextPathHolder').attr('data-contextPath') ? $('#contextPathHolder').attr('data-contextPath') : ''; - function notNullCheck(value) { return !(value === '' || value == null || (typeof value == 'object' && !Object.keys(value).length)); } diff --git a/src/main/resources/templates/thymeleaf/sample/egovSampleList.html b/src/main/resources/templates/thymeleaf/sample/egovSampleList.html index 4ae68d4..a3fdba1 100644 --- a/src/main/resources/templates/thymeleaf/sample/egovSampleList.html +++ b/src/main/resources/templates/thymeleaf/sample/egovSampleList.html @@ -14,7 +14,7 @@ function sampleSearch() { if (notNullCheck($('#searchKeyword').val())) { $('#pageIndex').val(1); - $('#listForm').attr('method', 'get').attr('action', contextPath + '/egovSampleList.do').submit(); + $('#listForm').attr('method', 'get').attr('action', [[@{/egovSampleList.do}]]).submit(); } else { alert(/*[[#{search.error}]]*/'검색어를 입력해주세요.'); } @@ -22,17 +22,17 @@ function samplePagination(pageIndex) { $('#pageIndex').val(pageIndex); - $('#listForm').attr('method', 'get').attr('action', contextPath + '/egovSampleList.do').submit(); + $('#listForm').attr('method', 'get').attr('action', [[@{/egovSampleList.do}]]).submit(); } function sampleCreate() { $('#id').val(null); - $('#listForm').attr('method', 'post').attr('action', contextPath + '/addSampleView.do').submit(); + $('#listForm').attr('method', 'post').attr('action', [[@{/addSampleView.do}]]).submit(); } function sampleDetail(id) { $('#id').val(id); - $('#listForm').attr('method', 'post').attr('action', contextPath + '/updateSampleView.do').submit(); + $('#listForm').attr('method', 'post').attr('action', [[@{/updateSampleView.do}]]).submit(); } diff --git a/src/main/resources/templates/thymeleaf/sample/egovSampleRegister.html b/src/main/resources/templates/thymeleaf/sample/egovSampleRegister.html index 5f87e2a..ba3b013 100644 --- a/src/main/resources/templates/thymeleaf/sample/egovSampleRegister.html +++ b/src/main/resources/templates/thymeleaf/sample/egovSampleRegister.html @@ -28,7 +28,7 @@ }); function sampleList() { - $('#detailForm').attr('method', 'get').attr('action', contextPath + '/egovSampleList.do').submit(); + $('#detailForm').attr('method', 'get').attr('action', [[@{/egovSampleList.do}]]).submit(); } function sampleAdd() { @@ -37,7 +37,7 @@ if (!validateSampleVO(frm)) { return; } else { - $('#detailForm').attr('method', 'post').attr('action', contextPath + '/addSample.do').submit(); + $('#detailForm').attr('method', 'post').attr('action', [[@{/addSample.do}]]).submit(); } } } @@ -48,14 +48,14 @@ if (!validateSampleVO(frm)) { return; } else { - $('#detailForm').attr('method', 'post').attr('action', contextPath + '/updateSample.do').submit(); + $('#detailForm').attr('method', 'post').attr('action', [[@{/updateSample.do}]]).submit(); } } } function sampleDelete() { if (confirm(/*[[#{delete.confirm}]]*/ '삭제하시겠습니까?')) { - $('#detailForm').attr('method', 'post').attr('action', contextPath + '/deleteSample.do').submit(); + $('#detailForm').attr('method', 'post').attr('action', [[@{/deleteSample.do}]]).submit(); } } diff --git a/src/test/java/egovframework/example/sample/web/EgovSampleControllerTestContextPathTest.java b/src/test/java/egovframework/example/sample/web/EgovSampleControllerTestContextPathTest.java new file mode 100644 index 0000000..c4b6133 --- /dev/null +++ b/src/test/java/egovframework/example/sample/web/EgovSampleControllerTestContextPathTest.java @@ -0,0 +1,68 @@ +package egovframework.example.sample.web; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.util.StringUtils; + +/** + * [게시판] 컨텍스트 패스 하위에 배포했을 때 화면이 만드는 폼 액션 URL 검증 + * + * @since 2026-09-09 + * + */ + +@SpringBootTest +@AutoConfigureMockMvc +class EgovSampleControllerTestContextPathTest { + + private static final String CONTEXT_PATH = "/sample"; + + @Autowired + private MockMvc mockMvc; + + @Test + void test_목록화면_폼액션에_컨텍스트패스가_붙는다() throws Exception { + final String html = render(get(CONTEXT_PATH + "/egovSampleList.do").contextPath(CONTEXT_PATH)); + + assertContextPath(html, "/egovSampleList.do"); + assertContextPath(html, "/addSampleView.do"); + assertContextPath(html, "/updateSampleView.do"); + } + + @Test + void test_등록화면_폼액션에_컨텍스트패스가_붙는다() throws Exception { + final String html = render(post(CONTEXT_PATH + "/addSampleView.do").contextPath(CONTEXT_PATH)); + + assertContextPath(html, "/egovSampleList.do"); + assertContextPath(html, "/addSample.do"); + assertContextPath(html, "/updateSample.do"); + assertContextPath(html, "/deleteSample.do"); + } + + /** + * 응답 본문을 읽는다. 자바스크립트 인라인 출력은 슬래시를 이스케이프하므로 되돌린다. + */ + private String render(final RequestBuilder requestBuilder) throws Exception { + return mockMvc.perform(requestBuilder).andExpect(status().isOk()).andReturn().getResponse() + .getContentAsString().replace("\\/", "/"); + } + + /** + * 화면에 나타난 해당 경로가 모두 컨텍스트 패스로 시작하는지 확인한다. + */ + private void assertContextPath(final String html, final String path) { + assertEquals(StringUtils.countOccurrencesOf(html, path), + StringUtils.countOccurrencesOf(html, CONTEXT_PATH + path), + path + " 폼 액션에 컨텍스트 패스가 빠졌다"); + } + +}