HTTP 에러 — 404와 500은 왜 다를까
브라우저에 빨간 에러 화면이 뜨는 순간은 개발자에게 가장 중요한 신호입니다. 숫자가 다르면 의심해야 할 지점도 달라집니다. 직접 눌러서 차이를 느껴봅시다.
상태코드 한눈에
| 코드 | 의미 | 언제 발생 | 누가 고쳐야 함 |
|---|---|---|---|
| 200 | OK · 정상 | 요청 처리 성공 | - |
| 404 | Not Found | Controller에 매핑 안 된 URL로 접속 | 라우팅 추가하거나 주소 오타 확인 |
| 400 | Bad Request | 요청 파라미터가 서버 규칙과 맞지 않음 | 프론트가 form 필드명/타입 점검 |
| 500 | Internal Server Error | 서버 코드 안에서 예외(Exception)가 터짐 | 백엔드가 stacktrace 추적 |
시뮬레이터 — 눌러서 직접 재현해보기
404가 났다 — 무엇을 의심해야 할까
"이 URL을 처리할 Servlet이 없다"는 뜻입니다. 서버는 잘 살아있고요.
Tomcat이 등록된 @WebServlet 어노테이션 표를 뒤져봐도 매칭되는 게 없으면 404를 돌려줍니다.
- 주소 오타? (
/boad,/loign같은 실수) - 해당 Servlet 파일을 만들었지만
@WebServlet("/login")어노테이션을 빼먹었나? - 어노테이션 경로 오타? (
@WebServlet("login")— 슬래시 빠짐) - JSP 파일명을 직접 친 경우 (
/login.jsp) 그 파일이 실제webapp안에 있는가?
주의 — 함정 하나: 만약 어떤 Servlet에
@WebServlet("/") 를 붙여놨다면,
그건 "default servlet" 이 되어서 매칭 안 된 URL까지 전부 그리로 끌려옵니다.
즉, /loign 오타를 쳐도 404가 안 나고 메인 페이지가 떠버려요.
이 경우엔 그 IndexServlet 안에서 path를 검사해서 직접 resp.sendError(404) 를 호출해줘야 합니다.
Servlet에서 명시적으로 404 던지는 코드 (필요할 때만)
// 예: IndexServlet이 @WebServlet("/")로 default servlet 역할일 때 String path = req.getRequestURI().substring(req.getContextPath().length()); if (!path.equals("/") && !path.isEmpty()) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); // 404 return; } // 정상이면 메인 화면으로 안내 resp.sendRedirect("main.jsp");
web.xml로 전용 404 페이지 연결하기
Tomcat의 기본 404 페이지 대신 우리 프로젝트 디자인에 맞는 JSP를 보여주려면
web.xml 에 <error-page> 를 등록해주면 됩니다.
<error-page> <error-code>404</error-code> <location>/error/404.jsp</location> </error-page>
500이 났다 — 무엇을 의심해야 할까
"주소는 맞게 왔는데, 그 안에서 내 코드가 터졌다"는 뜻입니다.
Controller 또는 Service/DAO 안에서 NullPointerException,
DB 연결 실패, SQL 문법 오류 같은 예외가 발생하면 Tomcat이 500을 돌려줍니다.
- DB가 꺼져 있나? 접속 정보(URL, user, password)가 틀렸나?
- SQL이 문법적으로 틀렸나? 컬럼명 오타?
- null 체크를 안 한 변수 접근?
- MyBatis mapper XML 경로가 안 맞나?
디버깅 시작점: 500이 나면 Eclipse의 Console 탭(또는 Tomcat log)에
반드시 stacktrace가 찍혀 있습니다. 빨간 글씨 중에서 Caused by: 라인을 먼저 읽으세요.
거기에 진짜 원인이 있습니다. 첫 줄의
500만 보고 포기하면 안 됩니다.
실제 서버 콘솔에서 보는 stacktrace 예시
SEVERE: Servlet.service() for servlet [LoginServlet] threw exception java.lang.RuntimeException: Failed to obtain JDBC Connection at com.edu.service.LoginService.login(LoginService.java:42) at com.edu.controller.LoginServlet.service(LoginServlet.java:25) ... Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost' at org.mariadb.jdbc.Driver.connect(Driver.java:89)
→ 이 경우 원인은 "코드가 이상해서"가 아니라 DB 접속 계정이 틀렸다는 겁니다. 500이 떴다고 Java 코드부터 뒤지면 시간 낭비입니다. Caused by를 읽는 습관이 중요합니다.
한 문장 요약
404는 "매칭되는 Servlet이 없어서" 나는 에러입니다. Servlet 코드는 실행되지 않았고 stacktrace도 없어요.
어노테이션 오타나 파일 누락을 의심하세요.
500은 "Servlet은 실행됐는데 그 안에서 예외가 던져진" 에러입니다. stacktrace가 있고 Caused by: 줄에 진짜 원인이 있어요. DB 연결, SQL 오류, NPE 같은 게 흔한 원인입니다.
이 구분이 디버깅의 출발점입니다.
500은 "Servlet은 실행됐는데 그 안에서 예외가 던져진" 에러입니다. stacktrace가 있고 Caused by: 줄에 진짜 원인이 있어요. DB 연결, SQL 오류, NPE 같은 게 흔한 원인입니다.
이 구분이 디버깅의 출발점입니다.