JSP Performance Optimization

JSP 성능 최적화

소개 (Introduction)

JSP(JavaServer Pages)는 서버 측에서 동적으로 콘텐츠를 생성하는 기술로, 성능 최적화는 애플리케이션의 반응 속도와 처리 능력을 향상시키기 위해 매우 중요합니다. 효과적인 성능 최적화는 더 나은 사용자 경험을 제공하며, 서버 자원의 효율적인 활용을 가능하게 합니다. 여기서는 JSP 애플리케이션의 성능을 최적화하는 다양한 방법과 예제를 상세히 설명합니다.

캐싱 (Caching)

캐싱은 자주 요청되는 데이터를 미리 저장하여, 동일한 요청이 있을 때 빠르게 응답하는 기술입니다.

예제: JSP 페이지 캐싱

JSP 페이지를 캐싱하여 정적 콘텐츠와 같이 취급할 수 있습니다. HTTP 헤더를 설정하여 캐싱을 제어할 수 있습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<%
// 캐싱 설정
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>
<% // 캐싱 설정 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>
<%
    // 캐싱 설정
    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를 사용한 커넥션 풀

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!-- 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"/>
<!-- 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"/>
<!-- 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"/>
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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)
}
// 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) }
// 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 페이지의 스크립트릿을 줄이고 성능을 최적화할 수 있습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<%@ 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>
<%@ 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>
<%@ 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)에서 제공하여 성능을 향상시킬 수 있습니다.

예제: 정적 콘텐츠 분리

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<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>
<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>
<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 역할 분리

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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);
}
}
// 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); } }
// 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);
    }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!-- 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>
<!-- 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>
<!-- 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 압축 설정

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!-- 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>
<!-- 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>
<!-- 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>
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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();
}
}
}
// 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(); } } }
// 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 응답 압축은 성능을 크게 향상시킬 수 있는 중요한 기법들입니다.

Leave a Reply

Your email address will not be published. Required fields are marked *