Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- Websocket
- jwt
- 0.75px border
- 컴포넌튼
- ES5
- angular
- entity
- 서버리스 #
- font-size
- es6
- 0.5px border
- 으
- 데이터베이스 #try #이중
- 10px
- 0.25px border
- 1px border
- 당근마켓
- ZOOM
- TS
- TypeScript
- 타입스크립트
- github
- Props
- literal
- Strict
- 문서번호
- npm
- 클론코딩
- 전역변수
- &연산
Archives
- Today
- Total
복잡한뇌구조마냥
[JAVA] 서블릿(Servlet) 본문
1) 서블릿이 뭐고, 왜 쓰나
서블릿은 Java로 작성된 서버 사이드 컴포넌트로, 브라우저 요청(HTTP)을 받고 응답(HTML/JSON 등)을 만들어 반환합니다.
JSP가 “화면 템플릿”에 가깝다면, 서블릿은 컨트롤러(요청 분기/비즈니스 로직 호출) 역할을 주로 담당합니다.
⚠️ Tomcat 10+에서는 패키지가 jakarta.servlet.* 입니다. (Tomcat 9 이하는 javax.servlet.*)
환경에 맞춰 import 경로만 바꾸면 됩니다.
2) 최소 예제: HelloServlet
// Tomcat 10+ (Jakarta) 기준
package com.example;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<!doctype html>");
out.println("<html><head><meta charset='UTF-8'><title>Hello</title></head>");
out.println("<body><h1>안녕하세요, Servlet!</h1></body></html>");
}
}
- @WebServlet("/hello") 로 라우팅
- doGet, doPost 등 HTTP 메서드별 처리 ( REST API )
3) JSP로 포워딩(Controller → View)
컨트롤러 서블릿에서 데이터를 request에 담아 JSP로 넘깁니다.
- java
@WebServlet("/articles")
public class ArticleListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 예시 데이터
var articles = java.util.List.of(
new Article(1, "첫 글"),
new Article(2, "둘째 글")
);
req.setAttribute("articles", articles);
req.getRequestDispatcher("/WEB-INF/views/article/list.jsp")
.forward(req, resp); // 서버 내부 포워드 (URL 안 바뀜)
}
static class Article {
public int id; public String title;
public Article(int id, String title) { this.id = id; this.title = title; }
public int getId(){ return id; } public String getTitle(){ return title; }
}
}
- jsp
<!-- /WEB-INF/views/article/list.jsp -->
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://jakarta.ee/jstl/core" %>
<!doctype html>
<html>
<head><meta charset="UTF-8"><title>게시글 목록</title></head>
<body>
<h1>게시글 목록</h1>
<table border="1" cellspacing="0" cellpadding="6">
<tbody>
<c:forEach var="article" items="${articles}">
<tr>
<td>${article.id}</td>
<td>
<a href="${pageContext.request.contextPath}/article/detail?id=${article.id}">
${article.title}
</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</body>
</html>
- 포워드는 서버 내부 이동이라 요청/응답이 유지됩니다 (속성도 유지).
- 보안상 JSP는 /WEB-INF 아래 두는 게 좋습니다(직접 접근 차단).
4) 상세 보기 & 파라미터 처리
- java
@WebServlet("/article/detail")
public class ArticleDetailServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String idParam = req.getParameter("id"); // e.g. ?id=2
if (idParam == null) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "id가 없습니다.");
return;
}
int id;
try { id = Integer.parseInt(idParam); }
catch (NumberFormatException e) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "id가 숫자가 아닙니다.");
return;
}
// 실제로는 Service/Repository에서 조회
var article = new Article(id, "상세 글 " + id);
req.setAttribute("article", article);
req.getRequestDispatcher("/WEB-INF/views/article/detail.jsp").forward(req, resp);
}
static class Article {
private final int id; private final String title;
Article(int id, String title){ this.id = id; this.title = title; }
public int getId(){ return id; } public String getTitle(){ return title; }
}
}
- jsp
<!-- /WEB-INF/views/article/detail.jsp -->
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!doctype html>
<html>
<head><meta charset="UTF-8"><title>상세</title></head>
<body>
<h1>게시글 상세</h1>
<div>번호: ${article.id}</div>
<div>제목: ${article.title}</div>
<p><a href="${pageContext.request.contextPath}/articles">목록으로</a></p>
</body>
</html>
5) 폼 처리(POST) + 리다이렉트
- java
@WebServlet("/article/create")
public class ArticleCreateServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.getRequestDispatcher("/WEB-INF/views/article/create.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String title = req.getParameter("title");
if (title == null || title.isBlank()) {
req.setAttribute("errorMessage", "제목을 입력하세요.");
req.getRequestDispatcher("/WEB-INF/views/article/create.jsp").forward(req, resp);
return;
}
// TODO: DB 저장 (Service 호출)
// 저장 후 PRG(Post/Redirect/Get) 패턴
resp.sendRedirect(req.getContextPath() + "/articles");
}
}
- jsp
<!-- /WEB-INF/views/article/create.jsp -->
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!doctype html>
<html>
<head><meta charset="UTF-8"><title>작성</title></head>
<body>
<h1>게시글 작성</h1>
<c:if test="${not empty errorMessage}">
<p style="color:red">${errorMessage}</p>
</c:if>
<form method="post" action="${pageContext.request.contextPath}/article/create">
<input type="text" name="title" placeholder="제목" />
<button type="submit">등록</button>
</form>
</body>
</html>
- 리다이렉트는 URL이 변경되고 새 요청으로 처리됩니다(새로고침 중복 방지).
6) 공통 인코딩/로그 처리: Filter
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class CharacterEncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
}
- 모든 요청에 UTF-8 강제 → 폼 한글 깨짐 방지
- 로깅/인증 체크도 필터에서 처리 가능
7) web.xml vs 애너테이션
(1) 애너테이션(@WebServlet/@WebFilter) — 요즘 기본
- 클래스 위에 바로 매핑. 간단하고 빠름.
(2) web.xml — 레거시/세밀한 설정에 유용
<!-- WEB-INF/web.xml -->
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" version="5.0">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.example.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
8) 추천 폴더 구조(예시)
src/main/java
└─ com.example
├─ filter
│ └─ CharacterEncodingFilter.java
├─ controller
│ ├─ ArticleListServlet.java
│ ├─ ArticleDetailServlet.java
│ └─ ArticleCreateServlet.java
├─ service
│ └─ ArticleService.java
└─ repository
└─ ArticleRepository.java
src/main/webapp
├─ WEB-INF
│ └─ views
│ └─ article
│ ├─ list.jsp
│ ├─ detail.jsp
│ └─ create.jsp
└─ index.html
- 컨트롤러-서비스-리포지토리 레이어 분리(MVC)
- JSP는 /WEB-INF/views 밑으로
9) 실무 팁 요약
- JSP에는 비즈니스 로직 넣지 않기: JSTL/EL로 화면만
- PRG 패턴으로 중복 제출 방지
- 필터로 UTF-8 고정
- Tomcat 10+면 jakarta.* 패키지 사용
- 공통 레이아웃은 include/커스텀 태그로 재사용
LIST
'BE > JAVA' 카테고리의 다른 글
[JAVA] Effective Java - 태그 달린 클래스보다는 클래스 계층구조를 활용하라 (23) (5) | 2025.08.11 |
---|---|
[JAVA] 객체 지향 설계의 5대 원칙 – SOLID (3) | 2025.08.10 |
[JAVA] JSP ( Java Server Pages ) (0) | 2025.08.07 |
[JAVA] Effective Java - clone 재정의는 주의해서 진행하라 (13) (4) | 2025.08.03 |
[JAVA] Effective Java - 다 쓴 객체 참조를 해제하라 (7) (3) | 2025.08.03 |