1. 프로젝트 배경
CBT 시험을 보기 전 수험자가 실제 시험 환경을 경험해볼 수 있도록 튜토리얼 기능을 구현.
튜토리얼은 수험자가 기본 환경을 점검하고 시험 방식을 익힐 수 있도록 다음 단계로 구성.
튜토리얼
접수정보 확인 → 로그인 → 화면 조정 → 키보드 확인 → 마우스 확인 → 문항 형태 → 사용법 → 예제 풀이
→ 모의시험 풀어보기 페이지로 연결
2. 초기 설계 — JSP 페이지 분리
각 단계를 별도의 JSP 페이지로 나누어 구현함.
- virtualTestStandby.jsp → 접수정보 확인
- virtualTestLogin.jsp → 로그인 화면
- tutoScreen.jsp → 화면 조정
- tutoKeyboard.jsp → 키보드 확인
- tutoMouse.jsp → 마우스 확인
- tutoItemTp.jsp → 문항 형태 확인
- tutoManual.jsp → 사용법 안내
- tutoMockTest.jsp → 예제 풀이
컨트롤러에서 view 파라미터를 받아 각 JSP로 분기하는 구조를 사용함.
// Controller 일부
@RequestMapping("/page.do")
public Object tutorial(HttpServletRequest request,
HttpServletResponse response,
@RequestParam(value="view", required=false) String view,
CHashMap param,
BindingAwareModelMapEx model) {
// 기본 페이지 설정
String resolved = (view == null || view.trim().isEmpty()) ? "tutoScreen" : view.trim();
// JSP 경로 매핑
String jspPath = VIEW_TO_JSP.getOrDefault(resolved, "/tutorial/" + resolved);
jspPath = this.pathInfo.getJspPath(jspPath);
model.addJspJsonValue("contentPage", "tutorial/" + resolved);
model.addJspJsonValue("currentView", resolved);
return setData(request, param, model, jspPath);
}
단계별 역할을 명확히 구분할 수 있었음.
파일 이름만 봐도 화면 용도를 쉽게 파악할 수 있어, 다른 개발자가 보더라도 이해하기 편리했음.
3. 피드백 — JSP 통합 필요성, 확인값 저장 기능 추가
피드백 1 : 컨트롤러에서 분기하는 구조보다 JSP 자체를 합쳐서 step 전환으로 처리하는 것이 좋다
하나의 tutorial.jsp 안에서 화면 전환만 스크립트로 처리하는 것이 더 단순하고 효율적이다.
- JSP 파일 수가 많아질수록 프로젝트 구조가 무거워짐
- 단순 안내 화면까지 모두 분리할 필요는 없음
- 유지보수 시 여러 파일을 동시에 열어야 해서 불편할 수 있음
피드백 2 : 사용자가 확인한 항목은 다시 반복해서 나오지 않도록 하는 것이 UX적으로 더 좋다
- 체크박스 값, 튜토리얼 확인값을 LocalStorage에 저장.
- 체크박스를 선택하지 않으면 다음단계로 넘어가지 않게 저장.
- 사용자가 이미 확인한 단계는 다시 보지 않아도 되므로 불필요한 반복을 줄임.
$(document).ready(function() {
j$(document).ready(function () {
const KEY = "checkboxChecked:tutoMouse";
const ctx = (j$.st.context || "");
// 체크박스 상태 복원
if (localStorage.getItem(KEY) === "true") {
j$("#mouse_Check").prop("checked", true);
}
// 체크박스 상태 저장
j$("#mouse_Check").on("change", function () {
if (j$(this).is(":checked")) localStorage.setItem(KEY, "true");
else localStorage.removeItem(KEY);
});
// 다음 버튼
j$("#btn_next").off("click").on("click", function (e) {
e.preventDefault();
if (!j$("#mouse_Check").is(":checked")) {
(j$.alert)('"PC 설정을 확인하였습니다(필수)" 체크해 주세요.');
return;
}
window.location.href = ctx + "page.do?view=tutoItemTp";
});
});
// (마우스 체크 박스)메뉴 이동 제한
const MUST_CHECKED = new Set(["사용법", "예제 풀이", "문항 형태"]);
j$(".lnb_menu a").on("click", function (e) {
const text = j$(this).text().trim();
if (MUST_CHECKED.has(text)) {
const checked = localStorage.getItem("checkboxChecked:tutoMouse") === "true";
if (!checked) {
e.preventDefault();
(j$.alert)(`마우스 확인의 체크박스를 선택해야 '${text}' 메뉴로 이동할 수 있습니다.`);
}
}
});
4. 회고 — 설계 의도와 배운 점
처음에는 다른 개발자가 보았을 때 용도가 명확하고, 유지보수가 쉬운 구조를 만들고자 JSP를 단계별로 모두 분리했음.
이 접근은 직관적이었고, 화면 역할을 이해하기 쉽게 만드는 데 효과적이었음.
하지만 실제로 규모 있는 프로젝트에 적용하다 보니
프로젝트 무게나 성능, 유지보수 효율성을 고려하지 못했다는 것을 깨닫게 됨.
당시에는 기능을 충분히 수행하는 것에만 집중했으나
피드백을 통해 시스템 전체의 크기와 구조적 단순화까지 고려해야 한다 는 점을 배우는 계기가 되었음.
5. 마무리
프로젝트 구조를 어떻게 잡아야 하는가에 대해 생각해볼 수 있는 기회였음.
- 작은 단위로 쪼개는 것만이 항상 좋은 것은 아님
- 프로젝트의 규모, 무게, 유지보수성을 종합적으로 고려해야 함
- 협업 환경에서는 가독성과 단순화의 균형이 필요함
앞으로는 기능 구현뿐 아니라 전체 시스템 구조를 고려한 설계를 지향할 계획임.
VIEW_WHITELIST = new HashSet<>();
VIEW_WHITELIST.add("tutoScreen");
VIEW_WHITELIST.add("tutoKeyboard");
VIEW_WHITELIST.add("tutoMouse");
VIEW_WHITELIST.add("tutoItemTp");
VIEW_WHITELIST.add("tutoManual");
VIEW_WHITELIST.add("tutoMockTest");
VIEW_WHITELIST.add("mockTestPrac");
VIEW_TO_JSP = new HashMap<>();
VIEW_TO_JSP.put("tutoScreen" , "/tutorial/tutoScreen");
VIEW_TO_JSP.put("tutoKeyboard", "/tutorial/tutoKeyboard");
VIEW_TO_JSP.put("tutoMouse" , "/tutorial/tutoMouse");
VIEW_TO_JSP.put("tutoItemTp" , "/tutorial/tutoItemTp");
VIEW_TO_JSP.put("tutoManual" , "/tutorial/tutoManual");
VIEW_TO_JSP.put("tutoMockTest", "/tutorial/tutoMockTest");
VIEW_TO_JSP.put("mockTestPrac", "/tutorial/mockTestPrac");
@RequestMapping("/page.do")
public Object tutorial(HttpServletRequest request,
HttpServletResponse response,
@RequestParam(value="view", required=false) String view,
CHashMap param,
BindingAwareModelMapEx model) {
// 좌석/로그인/상태 값 주입 제거
request.setAttribute("layoutPage", this.pathInfo.getJspPath("/cbt/examne/index"));
// 검증
String resolved = (view == null || view.trim().isEmpty()) ? "tutoScreen" : view.trim();
if (!VIEW_WHITELIST.contains(resolved)) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
String notFound = this.pathInfo.getJspPath("/error/404");
return setData(request, param, model, notFound);
}
// JSP 경로 매핑
String jspPath = VIEW_TO_JSP.getOrDefault(resolved, "/tutorial/" + resolved);
jspPath = this.pathInfo.getJspPath(jspPath);
model.addJspJsonValue("contentPage", "tutorial/" + resolved);
model.addJspJsonValue("currentView", resolved);
return setData(request, param, model, jspPath);
}
4. 회고 — 설계 의도와 배운 점
처음에는 **“다른 개발자가 보았을 때 용도가 명확하고, 유지보수가 쉬운 구조”**를 만들고자 JSP를 단계별로 모두 분리했음.
이 접근은 직관적이었고, 화면 역할을 이해하기 쉽게 만드는 데 효과적이었음.
하지만 실제로 규모 있는 프로젝트에 적용하다 보니,
프로젝트 무게나 성능, 유지보수 효율성을 고려하지 못했다는 한계를 깨닫게 됨.
당시에는 “기능을 충분히 수행하는 것”에만 집중했으나,
피드백을 통해 **“시스템 전체의 크기와 구조적 단순화까지 고려해야 한다”**는 점을 배우는 계기가 되었음.
⚡ 트러블슈팅과 배운 점
1. 로그인 오류 문제
튜토리얼 개발 과정에서 로그인 오류 현상이 발생함.
처음에는 원인을 특정하기 어려웠으나, 사무실과 집을 오가며 개발 환경을 바꾸는 과정에서 IP 주소 설정이 달라졌던 점이 원인으로 판단됨.
결국 네트워크 환경에 따라 세션이나 접근 경로가 달라지면서 로그인 리다이렉트 오류가 발생한 것이었음.
2. Swiper 적용 과정
마지막 예제 풀이와 모의시험 페이지를 구현하면서 Swiper를 도입함.
처음에는 기본 설정만으로 슬라이드 이동을 구현했으나, 실제 사용 시 민감도가 높아 의도치 않은 슬라이드 전환이 자주 발생하는 문제가 있었음.
이를 해결하기 위해 Swiper 옵션을 조정하여 민감도를 낮추는 설정을 적용함.
const swiper = new Swiper('.swiper-container', {
nextButton: '.question_next_btn',
prevButton: '.question_prev_btn',
threshold: 30, // 터치 이동 최소 거리 (민감도 낮춤)
touchRatio: 0.5, // 슬라이드 전환 민감도 조정
onSlideChangeEnd() {
updatePager(swiper);
syncTabs(swiper);
restoreSelectedVisualForSlide(swiper.activeIndex);
}
});
- threshold: 30 → 손가락/마우스를 일정 거리 이상 움직여야만 슬라이드가 넘어감
- touchRatio: 0.5 → 터치/드래그 민감도를 낮춰서 불필요한 전환 방지
이 설정을 통해 실제 시험 환경과 유사한 UX를 제공하면서도, 사용자의 실수로 인한 불편을 줄이는 방법을 배움.
3. 회고
- 로그인 오류를 통해 개발 환경(IP, 네트워크) 차이가 예상치 못한 문제를 유발할 수 있음을 경험함.
- Swiper 적용 과정에서 단순 기능 사용을 넘어서, 옵션 조정을 통해 UX 품질을 개선하는 방법을 배움.
'work' 카테고리의 다른 글
| Copilot으로 만드는 댓글 관리 API (0) | 2025.10.20 |
|---|---|
| 백엔드(copilot) 을 사용한 후 프론트엔드 (cusor) 로 연결 (0) | 2025.10.20 |
| 전자정부 프레임워크 4.3.0과 copilot 같이 쓰기 (0) | 2025.10.16 |
| 주간보고 페이지 수정 (0) | 2025.10.14 |
| 업무관리 프로젝트 (Figma make) (0) | 2025.10.13 |