JSP 성능 최적화
소개 (Introduction)
JSP(JavaServer Pages)는 서버 측에서 동적으로 콘텐츠를 생성하는 기술로, 성능 최적화는 애플리케이션의 반응 속도와 처리 능력을 향상시키기 위해 매우 중요합니다. 효과적인 성능 최적화는 더 나은 사용자 경험을 제공하며, 서버 자원의 효율적인 활용을 가능하게 합니다. 여기서는 JSP 애플리케이션의 성능을 최적화하는 다양한 방법과 예제를 상세히 설명합니다.
캐싱 (Caching)
캐싱은 자주 요청되는 데이터를 미리 저장하여, 동일한 요청이 있을 때 빠르게 응답하는 기술입니다.
예제: JSP 페이지 캐싱
JSP 페이지를 캐싱하여 정적 콘텐츠와 같이 취급할 수 있습니다. HTTP 헤더를 설정하여 캐싱을 제어할 수 있습니다.
<%
// 캐싱 설정
response.setHeader("Cache-Control", "max-age=3600, must-revalidate");
response.setHeader("Pragma", "cache");
response.setDateHeader("Expires", System.currentTimeMillis() + 3600000); // 1시간
%>
<html>
<head>
<title>캐싱 예제</title>
</head>
<body>
<h2>이 페이지는 캐싱됩니다.</h2>
<p>현재 시간: <%= new java.util.Date() %></p>
</body>
</html>
데이터베이스 연결 최적화 (Database Connection Optimization)
데이터베이스 연결을 효율적으로 관리하는 것은 성능 최적화의 중요한 부분입니다. 커넥션 풀(Connection Pool)을 사용하면 데이터베이스 연결을 재사용하여 성능을 향상시킬 수 있습니다.
예제: Apache DBCP를 사용한 커넥션 풀
<!-- context.xml 파일 -->
<Resource name="jdbc/mydb" auth="Container"
type="javax.sql.DataSource" maxTotal="20" maxIdle="10" maxWaitMillis="-1"
username="root" password="password" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb"/>
// UserDAO.java 파일에서 커넥션 풀 사용
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.Connection;
public class UserDAO {
private DataSource dataSource;
public UserDAO() {
try {
InitialContext ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/mydb");
} catch (Exception e) {
e.printStackTrace();
}
}
private Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 데이터베이스 작업 메서드들 (예: insertUser, getUser)
}
JSP 페이지 최적화 (JSP Page Optimization)
JSP 페이지를 최적화하여 성능을 향상시킬 수 있습니다. 이는 불필요한 연산을 줄이고, 효율적인 코드 작성으로 가능합니다.
예제: 표현 언어(Expression Language)와 JSTL 사용
표현 언어와 JSTL(JSP Standard Tag Library)을 사용하면, JSP 페이지의 스크립트릿을 줄이고 성능을 최적화할 수 있습니다.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>JSTL 예제</title>
</head>
<body>
<h2>JSTL을 사용한 예제</h2>
<c:set var="currentTime" value="<%= new java.util.Date() %>" />
<p>현재 시간: ${currentTime}</p>
</body>
</html>
정적 콘텐츠 분리 (Separation of Static Content)
정적 콘텐츠(이미지, CSS, JavaScript)는 별도의 서버나 CDN(Content Delivery Network)에서 제공하여 성능을 향상시킬 수 있습니다.
예제: 정적 콘텐츠 분리
<html>
<head>
<title>정적 콘텐츠 분리 예제</title>
<link rel="stylesheet" type="text/css" href="https://cdn.example.com/styles.css">
<script src="https://cdn.example.com/scripts.js"></script>
</head>
<body>
<h2>정적 콘텐츠 분리</h2>
<p>정적 파일은 CDN을 통해 제공됩니다.</p>
</body>
</html>
서블릿과 JSP의 역할 분리 (Separation of Servlet and JSP Roles)
서블릿은 비즈니스 로직과 데이터 처리를 담당하고, JSP는 데이터 표시를 담당하도록 역할을 분리합니다.
예제: 서블릿과 JSP 역할 분리
// UserServlet.java 파일
package com.example.controller;
import com.example.model.User;
import com.example.dao.UserDAO;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class UserServlet extends HttpServlet {
private UserDAO userDAO;
public void init() {
userDAO = new UserDAO();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getServletPath();
if ("/list".equals(action)) {
listUsers(request, response);
}
}
private void listUsers(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<User> userList = userDAO.getAllUsers();
request.setAttribute("userList", userList);
RequestDispatcher dispatcher = request.getRequestDispatcher("user-list.jsp");
dispatcher.forward(request, response);
}
}
<!-- user-list.jsp 파일 -->
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>사용자 목록</title>
</head>
<body>
<h2>사용자 목록</h2>
<table border="1">
<tr>
<th>사용자 이름</th>
<th>이메일</th>
</tr>
<c:forEach var="user" items="${userList}">
<tr>
<td>${user.username}</td>
<td>${user.email}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
압축 (Compression)
HTTP 응답을 압축하여 전송하는 것은 네트워크 대역폭을 줄이고 페이지 로드 속도를 높이는 데 도움이 됩니다.
예제: Gzip 압축 설정
<!-- web.xml 파일 -->
<filter>
<filter-name>GzipFilter</filter-name>
<filter-class>com.example.filter.GzipFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GzipFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
// GzipFilter.java 파일
package com.example.filter;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
public class GzipFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (acceptsGZipEncoding(httpRequest)) {
httpResponse.addHeader("Content-Encoding", "gzip");
GzipResponseWrapper gzipResponse = new GzipResponseWrapper(httpResponse);
chain.doFilter(request, gzipResponse);
gzipResponse.close();
} else {
chain.doFilter(request, response);
}
}
private boolean acceptsGZipEncoding(HttpServletRequest httpRequest) {
String acceptEncoding = httpRequest.getHeader("Accept-Encoding");
return acceptEncoding != null && acceptEncoding.contains("gzip");
}
}
// GzipResponseWrapper.java 파일
package com.example.filter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
public class GzipResponseWrapper extends HttpServletResponseWrapper {
private GZIPOutputStream gzipOutputStream = null;
private ServletOutputStream servletOutputStream = null;
public GzipResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (servletOutputStream == null) {
servletOutputStream = new GzipServletOutputStream(getResponse().getOutputStream());
}
return servletOutputStream;
}
public void close() throws IOException {
if (gzipOutputStream != null) {
gzipOutputStream.close();
}
}
class GzipServletOutputStream extends ServletOutputStream {
private GZIPOutputStream gzipOutputStream;
public GzipServletOutputStream(OutputStream output) throws IOException {
this.gzipOutputStream = new GZIPOutputStream(output);
}
@Override
public void write(int b) throws IOException {
gzipOutputStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
gzipOutputStream.write(b, off, len);
}
@Override
public void flush()
throws IOException {
gzipOutputStream.flush();
}
@Override
public void close() throws IOException {
gzipOutputStream.close();
}
}
}
위의 방법들을 통해 JSP 애플리케이션의 성능을 최적화할 수 있습니다. 캐싱, 데이터베이스 연결 최적화, JSP 페이지 최적화, 정적 콘텐츠 분리, 서블릿과 JSP의 역할 분리, 그리고 HTTP 응답 압축은 성능을 크게 향상시킬 수 있는 중요한 기법들입니다.
