이번 글에서는 JSP에서의 예외 처리에 대해 글을 작성해보려고 합니다. 아마 대부분의 사람들은 SpringBoot + Thymeleaf의 조합 혹은 프론트 프레임워크와의 조합으로 개발을 하여 JSP를 잘 사용하지 않을 것이라고 생각합니다. 하지만 아직 많은 레거시들은 JSP를 사용하고 있습니다. 사내 CMS도 레거시 Spring + JSP의 조합을 사용하고 있습니다. 저희 CMS는 JSP가 이미 만들어져 있는 것이 아닌, 각 페이지별로 문자열을 조합해 JSP를 만들고 있습니다. 그렇기 때문에 Controller에서 Model을 사용하는데 제한이 많아 JSP에 많은 자바 코드가 적혀있습니다. 이럴 때 로그인 처리 혹은 여러 가지 예외 상황을 어떻게 처리하는지에 대해 공부해보려고 합니다.
시나리오
Controller
아주 간단한 컨트롤러를 작성해 테스트해보겠습니다.
/user로 접근하면 하나의 사용자 객체를 생성 후 세션에 담아 user라는 뷰를 반환합니다.
/no-user로 접근하면 세션에 담긴 사용자 객체를 제거 후 user라는 뷰를 반환합니다.
@Controller
public class MainController {
@GetMapping("/user")
public String user(HttpServletRequest request) {
UserDto user = UserDto.builder()
.userId("potatowoong")
.userName("포테이토웅")
.build();
request.getSession().setAttribute("user", user);
return "user";
}
@GetMapping("/no-user")
public String noUser(HttpServletRequest request) {
request.getSession().removeAttribute("user");
return "user";
}
}
View
자바 영역에서 세션에 담긴 사용자 객체를 가져와 사용자 정보가 있으면 홍길동이라는 이름으로 변경 후 화면에 그려줍니다.
만약 사용자 객체가 존재하지 않으면 경고 문구와 함께 다른 url로 이동시킵니다.
<%
UserDto user = (UserDto) request.getSession().getAttribute("user");
pageContext.setAttribute("user", user);
if (user == null) {
%>
<script>
alert("로그인 정보가 없습니다.");
location.href = "/";
</script>
<%
}
user.setUserName("홍길동");
%>
<div style="display: flex; justify-content: center; margin-top: 400px;">
<div style="display: flex; flex-direction: column">
<p><span>아이디 : </span>${user.userId}</p>
<p><span>이름 : </span>${user.userName}</p>
</div>
</div>
/user로 접근
/user로 접근하면 정상적으로 사용자 객체의 정보를 꺼내 이름 변경 후 그려줍니다.
/no-user로 접근
/no-user로 접근하면 세션에 사용자 정보가 없으므로 경고 문구와 함께 다른 페이지로 이동할 것입니다. 하지만 예상과는 다르게 NPE가 발생하였습니다.
해당 오류에 대한 정답은 실행 순서에 있습니다. 전반적인 실행 순서는 다음과 같습니다.
- Java
- JSP
- JSTL
- Java Script
UserDto가 Null인 경우 실행 순서에 따라 Script 부분이 실행되는 게 아니라 user.setUserName이 먼저 실행되기 때문에 NPE가 발생하는 것입니다.
예외 상황에서 NPE를 방지하는 방법
개선 전 코드
아래 코드와 같이 return; 을 추가해 주면 간단하게 해결이 가능합니다. JSP 코드가 전부 실행되면서 <script>~~</script>를 그려줍니다. 그 후, return에 의해 NPE를 발생시키는 코드가 실행되지 않고 JS의 실행 순서로 넘어가면서 경고 메시지가 출력됩니다.
하지만 이 코드도 약간은 부족해 보입니다. 먼저, 가독성도 떨어지고, 이런 예외 처리 코드가 많아질수록 복잡해지면서 처리하기 힘들 것 같습니다.
<%
UserDto user = (UserDto) request.getSession().getAttribute("user");
pageContext.setAttribute("user", user);
if (user == null) {
%>
<script>
alert("로그인 정보가 없습니다.");
location.href = "/";
</script>
<%
return;
}
user.setUserName("홍길동");
%>
개선 후 코드
out.print()를 이용하면 더 간단하면서도 가독성 좋게 작성할 수 있습니다.
<%
UserDto user = (UserDto) request.getSession().getAttribute("user");
pageContext.setAttribute("user", user);
if (user == null) {
out.print("<script>alert('로그인 정보가 없습니다.'); location.href='/'</script>");
return;
}
user.setUserName("홍길동");
%>
📖 정리
개인적인 견해로 JSP 파일이 점차적으로 복잡해지고 유지보수가 어려워지는 경향이 있다고 생각합니다. Controller나 Service 영역과 비교했을 때, JSP는 프론트엔드와 백엔드가 혼합된 형태로, 복잡한 로직을 처리하기에는 적합하지 않을 수 있습니다.
Controller외 Service는 보다 모듈화되고 재사용 가능한 메소드로 구성되어 있는데 비해, JSP는 주로 화면을 렌더링하거나 사용자 입력을 처리하는 등의 UI 요소에 집중하기 때문에 비즈니스 로직이나 데이터 처리 로직을 작성하기에 적합하지 않을 수 있습니다.
이러한 상황에서 JSP 파일의 코드 리팩토링은 작고 사소한 단계일지라도, 이러한 작은 개선들이 코드의 품질을 향상시키고 유지보수성을 향상시키는데 도움이 될 수 있다고 생각합니다. 물론, 이렇게 작은 리팩토링을 하느라 굳이 시간을 들여야 할까라는 생각이 들기도 하지만, 이런 사소한 개선들을 고려하고 실행하는 것은 저 자신을 보다 나은 개발자로 성장시킬 수 있는 기회하고 생각합니다.
📝 예제 코드
Github - https://github.com/woong99/blog-example/tree/main/jsp-exception
'Programming > SpringBoot' 카테고리의 다른 글
[SpringBoot] JPA 동적 스키마 (1) | 2024.11.06 |
---|---|
[SpringBoot] @ModelAttribute 작동 원리 (0) | 2024.07.17 |
[SpringBoot] Entity의 ID를 테스트에서 삽입하는 방법 (0) | 2024.06.02 |
[SpringBoot] @ResponseBody 원리 (0) | 2024.01.29 |
[SpringBoot] Model VS ModelMap (0) | 2023.12.26 |