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 응답 압축은 성능을 크게 향상시킬 수 있는 중요한 기법들입니다.