Ver3.0 ๐ ์๋ฐ ์น ๊ฐ๋ฐ์ ๋ชจ๋ ๊ฒ

๐ ์๋ฐ ์น ๊ฐ๋ฐ์ ๋ชจ๋ ๊ฒ
์๋ ! ์ค๋์ ์๋ฐ ์น ๊ฐ๋ฐ์ ํต์ฌ ๊ธฐ์ ๋ค์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ ๊ฑฐ์ผ. โ ์๋ฐ๋ก ์น ๊ฐ๋ฐ์ ์์ํ๋ ค๋ ์ฌ๋์ด๋ผ๋ฉด ๋๊ตฌ๋ ํ ๋ฒ์ฏค์ Servlet, JSP, Spring MVC๋ผ๋ ๋จ์ด๋ฅผ ๋ค์ด๋ดค์ ๊ฑฐ์ผ. ๊ทผ๋ฐ ์ด๊ฒ ์ ํํ ๋ญ๊ณ , ์ด๋ป๊ฒ ์ฐ๊ฒฐ๋๋์ง ํท๊ฐ๋ฆฌ์ง ์์? ๊ฑฑ์ ๋ง! ์น๊ตฌํํ ์ค๋ช ํ๋ฏ์ด ์ฝ๊ณ ์ฌ๋ฏธ์๊ฒ ํ์ด์ค๊ฒ. ๐
๐ฏ ์๋ฐ ์น ๊ฐ๋ฐ์ ์์, Servlet์ด๋?
์๋ฐ๋ก ์น ๊ฐ๋ฐ์ ํ๋ค๋ ๊ฑด ๊ฒฐ๊ตญ ์ฌ์ฉ์์ ์์ฒญ์ ๋ฐ์์ ์ฒ๋ฆฌํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ๋ณด์ฌ์ฃผ๋ ๊ฑฐ์ผ. ์ด ๊ณผ์ ์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ด ๋๋ ๊ฒ ๋ฐ๋ก Servlet์ด์ผ. ๐ง
Servlet์ 1997๋
์ Sun Microsystems(์ง๊ธ์ Oracle)์์ ๋ฐํํ ์๋ฐ ๊ธฐ์ ์ด์ผ. ๋น์์๋ CGI(Common Gateway Interface)๋ผ๋ ๋ฐฉ์์ผ๋ก ์น ์๋ฒ์ ํ๋ก๊ทธ๋จ์ด ํต์ ํ๋๋ฐ, ์ด๊ฒ ์ฑ๋ฅ๋ ๋ณ๋ก๊ณ ๊ด๋ฆฌ๋ ์ด๋ ค์ ์ด. ๊ทธ๋์ ์๋ฐ ์ง์์์ "์ฐ๋ฆฌ๊ฐ ๋ ๋์ ๊ฑธ ๋ง๋ค์!"๋ผ๊ณ ํด์ ๋์จ ๊ฒ Servlet์ด์ง. ๐ก
Servlet์ ํต์ฌ ๊ฐ๋
Servlet์ ์๋ฐ๋ก ์์ฑ๋ ์๋ฒ ์ธก ํ๋ก๊ทธ๋จ์ด์ผ. ์น ์๋ฒ์์ ์คํ๋๋ฉด์ ํด๋ผ์ด์ธํธ(์ฃผ๋ก ์น ๋ธ๋ผ์ฐ์ )์ ์์ฒญ์ ๋ฐ์ ์ฒ๋ฆฌํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ HTML์ด๋ JSON ๊ฐ์ ํํ๋ก ์๋ตํด์ฃผ๋ ์ญํ ์ ํด. ๐
์ฝ๊ฒ ๋งํ๋ฉด, ์ฌ์ฉ์๊ฐ ์น ํ์ด์ง์์ ๋ฒํผ์ ํด๋ฆญํ๊ฑฐ๋ ํผ์ ์ ์ถํ๋ฉด, ๊ทธ ์์ฒญ์ด ์๋ฒ๋ก ๋ ์๊ฐ๊ณ , Servlet์ด ๊ทธ๊ฑธ ๋ฐ์์ "์, ์ด ์ฌ๋์ด ๋ก๊ทธ์ธํ๋ ค๊ณ ํ๋๊ตฌ๋" ๋๋ "์ํ ๋ชฉ๋ก์ ๋ณด๊ณ ์ถ์ด ํ๋๊ตฌ๋"๋ผ๊ณ ํ๋จํด์ ์ ์ ํ ์ฒ๋ฆฌ๋ฅผ ํ๋ ๊ฑฐ์ง.
Servlet์ ์๋ช ์ฃผ๊ธฐ (Lifecycle)
Servlet์ ํ์ด๋์ ์ฃฝ์ ๋๊น์ง ์ ํด์ง ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๊ณ ์์ด. ์ด๊ฑธ ์ดํดํ๋ฉด Servlet์ด ์ด๋ป๊ฒ ๋์ํ๋์ง ํจ์ฌ ๋ช ํํ๊ฒ ์ ์ ์์ด. ๐
1. ์ด๊ธฐํ ๋จ๊ณ (init)
Servlet ์ปจํ ์ด๋(Tomcat ๊ฐ์ ๊ฑฐ)๊ฐ Servlet ํด๋์ค๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ฉํ๊ณ , ์ธ์คํด์ค๋ฅผ ์์ฑํด. ๊ทธ๋ฆฌ๊ณ init() ๋ฉ์๋๋ฅผ ํธ์ถํด์ ์ด๊ธฐํ ์์ ์ ์ํํ์ง. ์ด ๊ณผ์ ์ Servlet์ด ์ฒ์ ์์ฒญ๋ ๋ ๋ฑ ํ ๋ฒ๋ง ์คํ๋ผ. ๐
public void init(ServletConfig config) throws ServletException {
// ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ค์
// ์ค์ ํ์ผ ๋ก๋ฉ
// ์ด๊ธฐ ๋ฆฌ์์ค ํ ๋น ๋ฑ
}
2. ์๋น์ค ๋จ๊ณ (service)
์ค์ ๋ก ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋จ๊ณ์ผ. service() ๋ฉ์๋๊ฐ ํธ์ถ๋๊ณ , ์ด ๋ฉ์๋๋ HTTP ์์ฒญ ๋ฐฉ์(GET, POST ๋ฑ)์ ๋ฐ๋ผ doGet(), doPost() ๊ฐ์ ๋ฉ์๋๋ฅผ ํธ์ถํด. ์ด ๋จ๊ณ๋ ์์ฒญ์ด ์ฌ ๋๋ง๋ค ๋ฐ๋ณต์ ์ผ๋ก ์คํ๋ผ. โก
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// GET ์์ฒญ ์ฒ๋ฆฌ
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<h1>Hello, Servlet!</h1>");
}
3. ์ข ๋ฃ ๋จ๊ณ (destroy)
Servlet ์ปจํ ์ด๋๊ฐ ์ข ๋ฃ๋๊ฑฐ๋ Servlet์ ๋ฉ๋ชจ๋ฆฌ์์ ์ ๊ฑฐํ ๋ destroy() ๋ฉ์๋๊ฐ ํธ์ถ๋ผ. ์ฌ๊ธฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ๋ซ๊ฑฐ๋, ํ์ผ์ ์ ๋ฆฌํ๋ ๋ฑ์ ๋ง๋ฌด๋ฆฌ ์์ ์ ํด. ์ด๊ฒ๋ ์์ ๋์ ๋ฑ ํ ๋ฒ๋ง ์คํ๋ผ. ๐
public void destroy() {
// ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ข
๋ฃ
// ๋ฆฌ์์ค ํด์
// ๋ก๊ทธ ๊ธฐ๋ก ๋ฑ
}
์ค์ Servlet ์์
์ด๋ก ๋ง ๋ณด๋ฉด ์ฌ๋ฏธ์์ผ๋๊น, ์ค์ ๋ก ๊ฐ๋จํ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ Servlet์ ๋ง๋ค์ด๋ณผ๊น? ๐จโ๐ป
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// 1. ์์ฒญ ํ๋ผ๋ฏธํฐ ๋ฐ๊ธฐ
String username = request.getParameter("username");
String password = request.getParameter("password");
// 2. ๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ
boolean isValid = validateUser(username, password);
// 3. ์๋ต ์์ฑ
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
if (isValid) {
// ์ธ์
์ ์ฌ์ฉ์ ์ ๋ณด ์ ์ฅ
HttpSession session = request.getSession();
session.setAttribute("user", username);
out.println("<h1>๋ก๊ทธ์ธ ์ฑ๊ณต!</h1>");
out.println("<p>ํ์ํฉ๋๋ค, " + username + "๋</p>");
} else {
out.println("<h1>๋ก๊ทธ์ธ ์คํจ</h1>");
out.println("<p>์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํ์ธ์.</p>");
}
}
private boolean validateUser(String username, String password) {
// ์ค์ ๋ก๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ
return "admin".equals(username) && "1234".equals(password);
}
}
๐ก Tip: @WebServlet ์ด๋ ธํ ์ด์ ์ Servlet 3.0๋ถํฐ ์ง์๋๋ ๊ธฐ๋ฅ์ด์ผ. ์ด์ ์๋ web.xml ํ์ผ์ ์ผ์ผ์ด ๋งคํ ์ ๋ณด๋ฅผ ์์ฑํด์ผ ํ๋๋ฐ, ์ด์ ๋ ์ด๋ ธํ ์ด์ ํ๋๋ก ๊ฐ๋จํ๊ฒ URL ๋งคํ์ ํ ์ ์์ด. ํจ์ฌ ํธ๋ฆฌํ์ง? ๐
Servlet์ ํ๊ณ
์ ์์ ๋ฅผ ๋ณด๋ฉด ๋ญ๊ฐ ์ด์ํ ์ ์ ๋๊ผ์ ๊ฑฐ์ผ. HTML ์ฝ๋๋ฅผ ์๋ฐ ์ฝ๋ ์์์ ๋ฌธ์์ด๋ก ์์ฑํ๊ณ ์์์? ๐ค
๋ง์, ์ด๊ฒ Servlet์ ๊ฐ์ฅ ํฐ ๋ฌธ์ ์ ์ด์ผ. ํ๋ฉด์ ๋ง๋ค๋ ค๋ฉด HTML, CSS, JavaScript๋ฅผ ๋ชจ๋ ์๋ฐ ์ฝ๋ ์์ ๋ฌธ์์ด๋ก ๋ฃ์ด์ผ ํ๋๋ฐ, ์ด๊ฒ ์ ๋ง ๋ถํธํ๊ณ ์ ์ง๋ณด์๋ ์ด๋ ค์. ๋์์ด๋์ ํ์
ํ๊ธฐ๋ ํ๋ค๊ณ , ์ฝ๋ ๊ฐ๋
์ฑ๋ ๋จ์ด์ง์ง.
์๋ฅผ ๋ค์ด, ๋ณต์กํ ํ
์ด๋ธ์ ๋ง๋ ๋ค๊ณ ์๊ฐํด๋ด:
out.println("<table border='1'>");
out.println(" <tr>");
out.println(" <th>๋ฒํธ</th>");
out.println(" <th>์ด๋ฆ</th>");
out.println(" <th>์ด๋ฉ์ผ</th>");
out.println(" </tr>");
for (User user : userList) {
out.println(" <tr>");
out.println(" <td>" + user.getId() + "</td>");
out.println(" <td>" + user.getName() + "</td>");
out.println(" <td>" + user.getEmail() + "</td>");
out.println(" </tr>");
}
out.println("</table>");
๋ณด๊ธฐ๋ง ํด๋ ๋จธ๋ฆฌ ์ํ์ง? ๐ต ์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ ๊ฒ ๋ฐ๋ก JSP์ผ!
๐จ ํ๋ฉด์ ์ํ ๊ธฐ์ , JSP (JavaServer Pages)
JSP๋ 1999๋
์ ๋ฑ์ฅํ ๊ธฐ์ ๋ก, Servlet์ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํด ๋ง๋ค์ด์ก์ด. ํต์ฌ ์์ด๋์ด๋ ๊ฐ๋จํด: "HTML ์์ ์๋ฐ ์ฝ๋๋ฅผ ๋ฃ์!" โจ
Servlet์ด "์๋ฐ ์ฝ๋ ์์ HTML์ ๋ฃ๋" ๋ฐฉ์์ด์๋ค๋ฉด, JSP๋ ๊ทธ ๋ฐ๋์ผ. HTML ํ์ผ์ฒ๋ผ ์๊ธด ํ์ผ์ ํ์ํ ๋ถ๋ถ๋ง ์๋ฐ ์ฝ๋๋ฅผ ์ฝ์
ํ๋ ๊ฑฐ์ง. ํจ์ฌ ์ง๊ด์ ์ด๊ณ ํธ๋ฆฌํ์ง ์์?
JSP์ ๋์ ์๋ฆฌ
์ฌ๋ฏธ์๋ ์ฌ์ค์, JSP๋ ๊ฒฐ๊ตญ Servlet์ผ๋ก ๋ณํ๋๋ค๋ ๊ฑฐ์ผ! ๐
1. ์ฌ์ฉ์๊ฐ JSP ํ์ด์ง๋ฅผ ์์ฒญํ๋ฉด
2. JSP ์ปจํ
์ด๋๊ฐ JSP ํ์ผ์ Servlet ์๋ฐ ์ฝ๋๋ก ๋ณํํด
3. ๊ทธ Servlet ์ฝ๋๋ฅผ ์ปดํ์ผํด์ ํด๋์ค ํ์ผ์ ๋ง๋ค๊ณ
4. ๊ทธ ํด๋์ค๋ฅผ ์คํํด์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํด
์ฒ์ ์์ฒญํ ๋๋ ์ด ๋ณํ ๊ณผ์ ๋๋ฌธ์ ์ฝ๊ฐ ๋๋ฆด ์ ์์ง๋ง, ํ ๋ฒ ๋ณํ๋๋ฉด ๊ทธ ๋ค์๋ถํฐ๋ ๋น ๋ฅด๊ฒ ์คํ๋ผ. ๋๋ํ์ง? ๐ง
JSP ๊ธฐ๋ณธ ๋ฌธ๋ฒ
JSP์๋ ์ฌ๋ฌ ๊ฐ์ง ํน์ํ ํ๊ทธ๋ค์ด ์์ด. ํ๋์ฉ ์ดํด๋ณด์! ๐
1. ์คํฌ๋ฆฝํ๋ฆฟ (Scriptlet) - <% %>
์๋ฐ ์ฝ๋๋ฅผ ์ง์ ์์ฑํ ์ ์๋ ์์ญ์ด์ผ. ๋ณ์ ์ ์ธ, ์กฐ๊ฑด๋ฌธ, ๋ฐ๋ณต๋ฌธ ๋ฑ์ ์์ ๋กญ๊ฒ ์ฌ์ฉํ ์ ์์ด.
<%
String username = "ํ๊ธธ๋";
int age = 25;
if (age >= 20) {
out.println("์ฑ์ธ์
๋๋ค.");
}
%>
2. ํํ์ (Expression) - <%= %>
๋ณ์๋ ๋ฉ์๋์ ๊ฒฐ๊ณผ๊ฐ์ ์ถ๋ ฅํ ๋ ์ฌ์ฉํด. out.println()์ ์ฐ์ง ์์๋ ์๋์ผ๋ก ์ถ๋ ฅ๋ผ.
<p>์ฌ์ฉ์ ์ด๋ฆ: <%= username %></p>
<p>๋์ด: <%= age %>์ธ</p>
<p>๋ด๋
๋์ด: <%= age + 1 %>์ธ</p>
3. ์ ์ธ๋ถ (Declaration) - <%! %>
ํด๋์ค ๋ ๋ฒจ์ ๋ณ์๋ ๋ฉ์๋๋ฅผ ์ ์ธํ ๋ ์ฌ์ฉํด. ์ฃผ๋ก ์ฌ๋ฌ ์์ฒญ์์ ๊ณต์ ํ ๋ฐ์ดํฐ๋ ์ ํธ๋ฆฌํฐ ๋ฉ์๋๋ฅผ ๋ง๋ค ๋ ์จ.
<%!
private int visitCount = 0;
public String formatDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
%>
4. ์ง์์ (Directive) - <%@ %>
JSP ํ์ด์ง์ ์ ์ฒด์ ์ธ ์ค์ ์ ๋ด๋นํด. ํ์ด์ง ์ธ์ฝ๋ฉ, import ๋ฌธ, ์๋ฌ ํ์ด์ง ์ง์ ๋ฑ์ ํ ์ ์์ด.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@ page import="java.util.*, java.text.*" %>
<%@ page errorPage="error.jsp" %>
์ค์ JSP ์์
์๊น Servlet์ผ๋ก ๋ง๋ค์๋ ๋ก๊ทธ์ธ ๊ฒฐ๊ณผ ํ๋ฉด์ JSP๋ก ๋ง๋ค์ด๋ณผ๊น? ํจ์ฌ ๊น๋ํด์ง ๊ฑฐ์ผ! โจ
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>๋ก๊ทธ์ธ ๊ฒฐ๊ณผ</title>
<style>
.success { color: green; }
.error { color: red; }
</style>
</head>
<body>
<%
String username = (String) session.getAttribute("user");
boolean isLoggedIn = (username != null);
%>
<% if (isLoggedIn) { %>
<div class="success">
<h1>๋ก๊ทธ์ธ ์ฑ๊ณต!</h1>
<p>ํ์ํฉ๋๋ค, <%= username %>๋</p>
<p>๋ง์ง๋ง ๋ก๊ทธ์ธ: <%= new java.util.Date() %></p>
</div>
<% } else { %>
<div class="error">
<h1>๋ก๊ทธ์ธ ์คํจ</h1>
<p>์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํ์ธ์.</p>
<a href="login.html">๋ค์ ๋ก๊ทธ์ธํ๊ธฐ</a>
</div>
<% } %>
</body>
</html>
์ด๋? Servlet ๋ฒ์ ๋ณด๋ค ํจ์ฌ ๋ณด๊ธฐ ์ข์ง? HTML ๊ตฌ์กฐ๊ฐ ๋ช ํํ๊ฒ ๋ณด์ด๊ณ , ์๋ฐ ์ฝ๋๋ ํ์ํ ๋ถ๋ถ์๋ง ๋ค์ด๊ฐ ์์ด. ๐
JSP ์ก์ ํ๊ทธ์ EL/JSTL
JSP๋ฅผ ๋ ๊ฐ๋ ฅํ๊ฒ ๋ง๋ค์ด์ฃผ๋ ๋๊ตฌ๋ค์ด ์์ด. ๋ฐ๋ก ์ก์ ํ๊ทธ, EL(Expression Language), JSTL(JSP Standard Tag Library)์ด์ผ. ๐ ๏ธ
EL (Expression Language)
EL์ JSP์์ ๋ฐ์ดํฐ๋ฅผ ๋ ์ฝ๊ฒ ์ ๊ทผํ๊ณ ์ถ๋ ฅํ๊ธฐ ์ํ ํํ ์ธ์ด์ผ. ${} ํํ๋ก ์ฌ์ฉํด.
<!-- ๊ธฐ์กด ๋ฐฉ์ -->
<%= request.getAttribute("username") %>
<!-- EL ๋ฐฉ์ -->
${username}
<!-- ๊ฐ์ฒด ์์ฑ ์ ๊ทผ -->
${user.name}
${user.email}
<!-- ์ฐ์ฐ๋ ๊ฐ๋ฅ -->
${price * quantity}
${age >= 20 ? "์ฑ์ธ" : "๋ฏธ์ฑ๋
์"}
ํจ์ฌ ๊ฐ๊ฒฐํ๊ณ ์ฝ๊ธฐ ์ฝ์ง? EL์ ์๋์ผ๋ก null ์ฒดํฌ๋ ํด์ฃผ๊ณ , ํ๋ณํ๋ ์์์ ํด์ค์ ์ ๋ง ํธ๋ฆฌํด. ๐
JSTL (JSP Standard Tag Library)
JSTL์ ๋ฐ๋ณต๋ฌธ, ์กฐ๊ฑด๋ฌธ, ํฌ๋งทํ ๋ฑ ์์ฃผ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ๋ค์ ํ๊ทธ ํํ๋ก ์ ๊ณตํด. ์คํฌ๋ฆฝํ๋ฆฟ์ ์ฐ์ง ์๊ณ ๋ ๋ณต์กํ ๋ก์ง์ ์ฒ๋ฆฌํ ์ ์์ด.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- ์กฐ๊ฑด๋ฌธ -->
<c:if test="${age >= 20}">
<p>์ฑ์ธ์
๋๋ค.</p>
</c:if>
<c:choose>
<c:when test="${score >= 90}">
<p>A ํ์ </p>
</c:when>
<c:when test="${score >= 80}">
<p>B ํ์ </p>
</c:when>
<c:otherwise>
<p>C ํ์ </p>
</c:otherwise>
</c:choose>
<!-- ๋ฐ๋ณต๋ฌธ -->
<table>
<c:forEach var="user" items="${userList}">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.email}</td>
</tr>
</c:forEach>
</table>
JSTL์ ์ฌ์ฉํ๋ฉด JSP ํ์ด์ง์์ ์๋ฐ ์ฝ๋๋ฅผ ๊ฑฐ์ ์ฌ์ฉํ์ง ์์๋ ๋ผ. ์ฝ๋๊ฐ ํจ์ฌ ๊น๋ํด์ง๊ณ , ๋์์ด๋๋ ์ดํดํ๊ธฐ ์ฌ์์ ธ. ๐จ
JSP์ ํ๊ณ์ ๋ฌธ์ ์
JSP๊ฐ Servlet๋ณด๋ค ํจ์ฌ ํธ๋ฆฌํ๊ธด ํ์ง๋ง, ์ฌ์ ํ ๋ฌธ์ ๊ฐ ์์ด. ๐ค
1. ๋น์ฆ๋์ค ๋ก์ง๊ณผ ํ๋ฉด์ด ์์
JSP ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ ์ฝ๋, ๊ณ์ฐ ๋ก์ง ๋ฑ์ ๋ฃ๋ค ๋ณด๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ ธ.
2. ํ
์คํธ๊ฐ ์ด๋ ค์
JSP๋ ์น ์ปจํ
์ด๋ ์์์๋ง ์คํ๋๊ธฐ ๋๋ฌธ์ ๋จ์ ํ
์คํธ๋ฅผ ์์ฑํ๊ธฐ ํ๋ค์ด.
3. ์ฌ์ฌ์ฉ์ฑ์ด ๋จ์ด์ง
๋น์ทํ ์ฝ๋๋ฅผ ์ฌ๋ฌ JSP ํ์ผ์ ๋ณต์ฌํด์ ๋ถ์ฌ๋ฃ๊ฒ ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์.
์ด๋ฐ ๋ฌธ์ ๋ค์ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ ๊ฐ๋
์ด ๋ฐ๋ก MVC ํจํด์ด์ผ! ๐ฏ
๐๏ธ MVC ํจํด์ ๋ฑ์ฅ
MVC๋ Model-View-Controller์ ์ฝ์๋ก, ์ ํ๋ฆฌ์ผ์ด์
์ ์ธ ๊ฐ์ง ์ญํ ๋ก ๋ถ๋ฆฌํ๋ ๋์์ธ ํจํด์ด์ผ. ๐ญ
Model (๋ชจ๋ธ): ๋ฐ์ดํฐ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ด๋นํด. ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํต์ ํ๊ณ , ๊ณ์ฐ์ ์ํํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ๋ ์ญํ ์ด์ง.
View (๋ทฐ): ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง๋ ํ๋ฉด์ ๋ด๋นํด. JSP๊ฐ ์ฌ๊ธฐ์ ํด๋นํ๊ณ , ์์ํ๊ฒ ๋ฐ์ดํฐ๋ฅผ ํ์ํ๋ ์ญํ ๋ง ํด.
Controller (์ปจํธ๋กค๋ฌ): ์ฌ์ฉ์์ ์์ฒญ์ ๋ฐ์์ Model๊ณผ View๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ ์ค๊ฐ ๊ด๋ฆฌ์ ์ญํ ์ด์ผ. Servlet์ด ์ฃผ๋ก ์ด ์ญํ ์ ๋งก์.
์ด๋ ๊ฒ ๋ถ๋ฆฌํ๋ฉด ๊ฐ ๋ถ๋ถ์ ๋
๋ฆฝ์ ์ผ๋ก ๊ฐ๋ฐํ๊ณ ํ
์คํธํ ์ ์์ด์ ํจ์ฌ ํจ์จ์ ์ด์ผ! ๐
MVC ํจํด ์ค์ ์์
๊ฐ๋จํ ๊ฒ์ํ ๋ชฉ๋ก ์กฐํ ๊ธฐ๋ฅ์ MVC ํจํด์ผ๋ก ๋ง๋ค์ด๋ณผ๊น? ๐
Model - Board.java (๋ฐ์ดํฐ ํด๋์ค)
public class Board {
private int id;
private String title;
private String content;
private String author;
private Date createdDate;
// ์์ฑ์, getter, setter ์๋ต
}
Model - BoardDAO.java (๋ฐ์ดํฐ ์ ๊ทผ ๊ฐ์ฒด)
public class BoardDAO {
private Connection conn;
public List<Board> getAllBoards() {
List<Board> boards = new ArrayList<>();
String sql = "SELECT * FROM board ORDER BY created_date DESC";
try (PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
Board board = new Board();
board.setId(rs.getInt("id"));
board.setTitle(rs.getString("title"));
board.setContent(rs.getString("content"));
board.setAuthor(rs.getString("author"));
board.setCreatedDate(rs.getDate("created_date"));
boards.add(board);
}
} catch (SQLException e) {
e.printStackTrace();
}
return boards;
}
}
Controller - BoardListServlet.java
@WebServlet("/board/list")
public class BoardListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// 1. Model์ ํตํด ๋ฐ์ดํฐ ์กฐํ
BoardDAO dao = new BoardDAO();
List<Board> boards = dao.getAllBoards();
// 2. ๋ฐ์ดํฐ๋ฅผ request์ ์ ์ฅ
request.setAttribute("boards", boards);
// 3. View๋ก ํฌ์๋ฉ
RequestDispatcher dispatcher =
request.getRequestDispatcher("/WEB-INF/views/boardList.jsp");
dispatcher.forward(request, response);
}
}
View - boardList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>๊ฒ์ํ ๋ชฉ๋ก</title>
</head>
<body>
<h1>๊ฒ์ํ</h1>
<table border="1">
<tr>
<th>๋ฒํธ</th>
<th>์ ๋ชฉ</th>
<th>์์ฑ์</th>
<th>์์ฑ์ผ</th>
</tr>
<c:forEach var="board" items="${boards}">
<tr>
<td>${board.id}</td>
<td>${board.title}</td>
<td>${board.author}</td>
<td>${board.createdDate}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
์ด๋ ๊ฒ ๋ถ๋ฆฌํ๋๊น ๊ฐ ๋ถ๋ถ์ ์ญํ ์ด ๋ช ํํ์ง? Model์ ๋ฐ์ดํฐ๋ง, View๋ ํ๋ฉด๋ง, Controller๋ ํ๋ฆ ์ ์ด๋ง ๋ด๋นํด. ๋์ค์ ํ๋ฉด ๋์์ธ์ ๋ฐ๊พธ๊ณ ์ถ์ผ๋ฉด JSP๋ง ์์ ํ๋ฉด ๋๊ณ , ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์กฐ๊ฐ ๋ฐ๋๋ฉด DAO๋ง ์์ ํ๋ฉด ๋ผ. ๐
๐ Spring MVC ํ๋ ์์ํฌ์ ๋ฑ์ฅ
MVC ํจํด์ผ๋ก ๊ฐ๋ฐํ๋ฉด ์ข๊ธด ํ๋ฐ, ๋งค๋ฒ Servlet์ ๋ง๋ค๊ณ web.xml์ ๋ฑ๋กํ๊ณ , ๊ณตํต ๊ธฐ๋ฅ์ ์ฒ๋ฆฌํ๋ ์ฝ๋๋ฅผ ๋ฐ๋ณตํด์ ์์ฑํ๋ ๊ฒ ๋ฒ๊ฑฐ๋ก์. ๊ทธ๋์ ๋ฑ์ฅํ ๊ฒ ๋ฐ๋ก Spring MVC ํ๋ ์์ํฌ์ผ! ๐
Spring MVC๋ 2004๋
์ ์ฒ์ ๋ฑ์ฅํ ์ดํ, ์๋ฐ ์น ๊ฐ๋ฐ์ ์ฌ์ค์ ํ์ค์ด ๋์์ด. ์ ์ธ๊ณ ์๋ง์ ๊ธฐ์
๋ค์ด Spring์ ์ฌ์ฉํ๊ณ ์๊ณ , ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ Spring ๊ฐ๋ฐ์๋ฅผ ๋ง์ด ์ฐพ๊ณ ์์ง. ๐ผ
Spring MVC์ ํต์ฌ ๊ฐ๋
1. DispatcherServlet (ํ๋ก ํธ ์ปจํธ๋กค๋ฌ)
Spring MVC์ ํต์ฌ์ DispatcherServlet์ด์ผ. ์ด๊ฒ ๋ชจ๋ ์์ฒญ์ ๋ฐ์์ ์ ์ ํ ์ปจํธ๋กค๋ฌ๋ก ๋ถ๋ฐฐํด์ฃผ๋ ์ญํ ์ ํด. ์ฐ๋ฆฌ๊ฐ ์ง์ Servlet์ ๋ง๋ค ํ์๊ฐ ์์ด์ ธ! ๐ฏ
DispatcherServlet์ ๋ค์๊ณผ ๊ฐ์ ์ผ๋ค์ ์๋์ผ๋ก ์ฒ๋ฆฌํด์ค:
- ์์ฒญ URL ๋ถ์
- ์ ์ ํ ์ปจํธ๋กค๋ฌ ์ฐพ๊ธฐ
- ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
- ๋ทฐ ๋ฆฌ์กธ๋น
- ์์ธ ์ฒ๋ฆฌ
- ์ธํฐ์
ํฐ ์คํ
2. @Controller์ @RequestMapping
Spring์์๋ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด์ ์ปจํธ๋กค๋ฌ๋ฅผ ์ ์ํด. @Controller ์ด๋ ธํ ์ด์ ์ ํด๋์ค์ ๋ถ์ด๊ณ , @RequestMapping์ผ๋ก URL์ ๋งคํํ๋ฉด ๋! โจ
@Controller
@RequestMapping("/board")
public class BoardController {
@Autowired
private BoardService boardService;
@GetMapping("/list")
public String list(Model model) {
List<Board> boards = boardService.getAllBoards();
model.addAttribute("boards", boards);
return "board/list"; // ๋ทฐ ์ด๋ฆ
}
@GetMapping("/detail/{id}")
public String detail(@PathVariable int id, Model model) {
Board board = boardService.getBoard(id);
model.addAttribute("board", board);
return "board/detail";
}
@PostMapping("/write")
public String write(@ModelAttribute Board board) {
boardService.saveBoard(board);
return "redirect:/board/list";
}
}
๋ณด์ด์ง? Servlet์ ์์๋ฐ์ง๋ ์๊ณ , doGet/doPost ๋ฉ์๋๋ ์์ด. ๊ทธ๋ฅ ์ผ๋ฐ ๋ฉ์๋์ฒ๋ผ ์์ฑํ๋ฉด Spring์ด ์์์ ์ฒ๋ฆฌํด์ค. ์ ๋ง ํธ๋ฆฌํ์ง? ๐
3. ์์กด์ฑ ์ฃผ์ (Dependency Injection)
Spring์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋๊ฐ ๋ฐ๋ก ์์กด์ฑ ์ฃผ์ ์ด์ผ. @Autowired ์ด๋ ธํ ์ด์ ๋ง ๋ถ์ด๋ฉด ํ์ํ ๊ฐ์ฒด๋ฅผ ์๋์ผ๋ก ์ฃผ์ ํด์ค. ๐
@Service
public class BoardService {
@Autowired
private BoardDAO boardDAO;
public List<Board> getAllBoards() {
return boardDAO.getAllBoards();
}
public Board getBoard(int id) {
return boardDAO.getBoard(id);
}
@Transactional
public void saveBoard(Board board) {
boardDAO.saveBoard(board);
}
}
์ง์ new BoardDAO() ๊ฐ์ ์ฝ๋๋ฅผ ์ฐ์ง ์์๋ ๋ผ. Spring ์ปจํ ์ด๋๊ฐ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํด์ฃผ๊ธฐ ๋๋ฌธ์, ์ฐ๋ฆฌ๋ ๋น์ฆ๋์ค ๋ก์ง์๋ง ์ง์คํ ์ ์์ด. ๐ฏ
Spring MVC์ ์์ฒญ ์ฒ๋ฆฌ ํ๋ฆ
๋ณต์กํด ๋ณด์ด์ง๋ง, ์ค์ ๋ก ๊ฐ๋ฐ์๊ฐ ์ ๊ฒฝ ์จ์ผ ํ ๋ถ๋ถ์ 5๋ฒ Controller์ 6๋ฒ Service ์ ๋์ผ. ๋๋จธ์ง๋ Spring์ด ์๋์ผ๋ก ์ฒ๋ฆฌํด์ค! ๐ค
Spring MVC์ ์ฃผ์ ์ด๋ ธํ ์ด์
Spring MVC๋ฅผ ์ฌ์ฉํ ๋ ์์ฃผ ์ฐ๋ ์ด๋ ธํ ์ด์ ๋ค์ ์ ๋ฆฌํด๋ณผ๊ฒ. ๐
| ์ด๋ ธํ ์ด์ | ์ค๋ช | ์ฌ์ฉ ์์ |
|---|---|---|
| @Controller | ์ปจํธ๋กค๋ฌ ํด๋์ค ์ง์ | @Controller public class UserController { } |
| @RestController | RESTful API ์ปจํธ๋กค๋ฌ (JSON ์๋ต) |
@RestController public class ApiController { } |
| @RequestMapping | URL ๋งคํ (๋ชจ๋ HTTP ๋ฉ์๋) | @RequestMapping("/user") |
| @GetMapping | GET ์์ฒญ ๋งคํ | @GetMapping("/list") |
| @PostMapping | POST ์์ฒญ ๋งคํ | @PostMapping("/save") |
| @PutMapping | PUT ์์ฒญ ๋งคํ (์์ ) | @PutMapping("/update/{id}") |
| @DeleteMapping | DELETE ์์ฒญ ๋งคํ (์ญ์ ) | @DeleteMapping("/delete/{id}") |
| @RequestParam | ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ | @RequestParam String name |
| @PathVariable | URL ๊ฒฝ๋ก ๋ณ์ ๋ฐ์ธ๋ฉ | @PathVariable int id |
| @ModelAttribute | ํผ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด๋ก ๋ฐ์ธ๋ฉ | @ModelAttribute User user |
| @RequestBody | HTTP ์์ฒญ ๋ณธ๋ฌธ์ ๊ฐ์ฒด๋ก ๋ณํ | @RequestBody User user |
| @ResponseBody | ๋ฐํ๊ฐ์ HTTP ์๋ต ๋ณธ๋ฌธ์ผ๋ก | @ResponseBody String result |
์ค์ Spring MVC ์์
์ด์ ์ค์ ๋ก Spring MVC๋ก RESTful API๋ฅผ ๋ง๋ค์ด๋ณผ๊น? ์ฌ์ฉ์ ๊ด๋ฆฌ API๋ฅผ ์์ ๋ก ๋ค์ด๋ณผ๊ฒ. ๐จโ๐ป
Entity ํด๋์ค
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String email;
@CreatedDate
private LocalDateTime createdDate;
// getter, setter, ์์ฑ์ ์๋ต
}
Repository ์ธํฐํ์ด์ค
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
boolean existsByUsername(String username);
boolean existsByEmail(String email);
List<User> findByEmailContaining(String keyword);
}
JPA๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ๋ณธ์ ์ธ CRUD ๋ฉ์๋๋ ์๋์ผ๋ก ์ ๊ณต๋๊ณ , ๋ฉ์๋ ์ด๋ฆ๋ง์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์์ด. ์ ๋ง ํธ๋ฆฌํ์ง? โจ
Service ํด๋์ค
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User getUser(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."));
}
public User createUser(UserDto userDto) {
// ์ค๋ณต ์ฒดํฌ
if (userRepository.existsByUsername(userDto.getUsername())) {
throw new DuplicateUserException("์ด๋ฏธ ์กด์ฌํ๋ ์ฌ์ฉ์๋ช
์
๋๋ค.");
}
// ๋น๋ฐ๋ฒํธ ์ํธํ
User user = new User();
user.setUsername(userDto.getUsername());
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
user.setEmail(userDto.getEmail());
return userRepository.save(user);
}
public User updateUser(Long id, UserDto userDto) {
User user = getUser(id);
user.setEmail(userDto.getEmail());
return userRepository.save(user);
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
Controller ํด๋์ค
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
// ์ ์ฒด ์ฌ์ฉ์ ์กฐํ
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}
// ํน์ ์ฌ์ฉ์ ์กฐํ
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUser(id);
return ResponseEntity.ok(user);
}
// ์ฌ์ฉ์ ์์ฑ
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody UserDto userDto) {
User user = userService.createUser(userDto);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
// ์ฌ์ฉ์ ์์
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable Long id,
@Valid @RequestBody UserDto userDto) {
User user = userService.updateUser(id, userDto);
return ResponseEntity.ok(user);
}
// ์ฌ์ฉ์ ์ญ์
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
// ์์ธ ์ฒ๋ฆฌ
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException e) {
ErrorResponse error = new ErrorResponse("USER_NOT_FOUND", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}
์ด๋ ๊ฒ ์์ฑํ๋ฉด RESTful API๊ฐ ์์ฑ๋ผ! ๊ฐ ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ์ HTTP ์์ฒญ์ ์๋ตํด:
- GET /api/users โ ์ ์ฒด ์ฌ์ฉ์ ๋ชฉ๋ก
- GET /api/users/1 โ ID๊ฐ 1์ธ ์ฌ์ฉ์ ์ ๋ณด
- POST /api/users โ ์ ์ฌ์ฉ์ ์์ฑ
- PUT /api/users/1 โ ID๊ฐ 1์ธ ์ฌ์ฉ์ ์์
- DELETE /api/users/1 โ ID๊ฐ 1์ธ ์ฌ์ฉ์ ์ญ์
๊น๋ํ๊ณ ์ง๊ด์ ์ด์ง? ๐
Spring Boot์ ๋ฑ์ฅ
Spring MVC๋ ์ถฉ๋ถํ ๊ฐ๋ ฅํ์ง๋ง, ์ค์ ์ด ๋ณต์กํ๋ค๋ ๋จ์ ์ด ์์์ด. XML ์ค์ ํ์ผ์ ์์ฑํ๊ณ , ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์กด์ฑ์ ๊ด๋ฆฌํ๊ณ , ์๋ฒ๋ฅผ ์ค์ ํ๋ ๊ฒ ๋ฒ๊ฑฐ๋ก์ ์ง. ๐
๊ทธ๋์ 2014๋
์ Spring Boot๊ฐ ๋ฑ์ฅํ์ด! Spring Boot๋ "์ค์ ๋ณด๋ค ๊ด๋ก(Convention over Configuration)"๋ผ๋ ์ฒ ํ์ผ๋ก, ๋ณต์กํ ์ค์ ์์ด๋ ๋ฐ๋ก ์คํ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค ์ ์๊ฒ ํด์ค. ๐
Spring Boot์ ์ฃผ์ ํน์ง
1. ์๋ ์ค์ (Auto Configuration)
ํด๋์คํจ์ค์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณด๊ณ ์๋์ผ๋ก ์ค์ ์ ํด์ค. ์๋ฅผ ๋ค์ด, H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์์ผ๋ฉด ์๋์ผ๋ก ๋ฐ์ดํฐ์์ค๋ฅผ ์ค์ ํ๊ณ , Thymeleaf๊ฐ ์์ผ๋ฉด ๋ทฐ ๋ฆฌ์กธ๋ฒ๋ฅผ ์ค์ ํด์ค.
2. ๋ด์ฅ ์๋ฒ
Tomcat, Jetty, Undertow ๊ฐ์ ์๋ฒ๊ฐ ๋ด์ฅ๋์ด ์์ด์ ๋ณ๋๋ก ์๋ฒ๋ฅผ ์ค์นํ ํ์๊ฐ ์์ด. JAR ํ์ผ ํ๋๋ก ์คํ ๊ฐ๋ฅํด!
3. ์คํํฐ ์์กด์ฑ
spring-boot-starter-web, spring-boot-starter-data-jpa ๊ฐ์ ์คํํฐ๋ฅผ ์ถ๊ฐํ๋ฉด ํ์ํ ๋ชจ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋์ผ๋ก ํฌํจ๋ผ.
4. ํ๋ก๋์
์ค๋น ๊ธฐ๋ฅ
ํฌ์ค ์ฒดํฌ, ๋ฉํธ๋ฆญ, ๋ชจ๋ํฐ๋ง ๊ฐ์ ์ด์์ ํ์ํ ๊ธฐ๋ฅ๋ค์ด ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต๋ผ.
Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์์ํ๊ธฐ
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
์ด๊ฒ ์ ๋ถ์ผ! ์ด ์ฝ๋๋ง ์์ผ๋ฉด Spring Boot ์ ํ๋ฆฌ์ผ์ด์
์ด ์คํ๋ผ. @SpringBootApplication ์ด๋
ธํ
์ด์
ํ๋๊ฐ ๋ค์ ์ธ ๊ฐ์ง๋ฅผ ํฌํจํ๊ณ ์์ด:
- @Configuration: ์ค์ ํด๋์ค์์ ํ์
- @EnableAutoConfiguration: ์๋ ์ค์ ํ์ฑํ
- @ComponentScan: ์ปดํฌ๋ํธ ์ค์บ ํ์ฑํ
์ ๋ง ๊ฐ๋จํ์ง? ๐
application.properties ์ค์
Spring Boot์์๋ XML ๋์ application.properties ๋๋ application.yml ํ์ผ๋ก ์ค์ ์ ๊ด๋ฆฌํด. ํจ์ฌ ๊ฐ๋จํ๊ณ ์ง๊ด์ ์ด์ผ. ๐
# ์๋ฒ ํฌํธ ์ค์
server.port=8080
# ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA ์ค์
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
# ๋ก๊น
๋ ๋ฒจ
logging.level.root=INFO
logging.level.com.myapp=DEBUG
# ํ์ผ ์
๋ก๋ ์ค์
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
# Thymeleaf ์ค์
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
์ด๋ ๊ฒ ๊ฐ๋จํ ์ค์ ๋ง์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ, JPA ์ค์ , ๋ก๊น , ํ์ผ ์ ๋ก๋ ๋ฑ ๋ชจ๋ ๊ฒ ๋์ํด. ์์ ์๋ ์ด๋ฐ ์ค์ ์ XML๋ก ์์ญ ์ค์ฉ ์์ฑํด์ผ ํ๋๋ฐ, ์ ๋ง ํธํด์ก์ง? ๐
๐ฅ ์ต์ ํธ๋ ๋์ ์ค๋ฌด ํ
์ด์ ์ค๋ฌด์์ ์์ฃผ ์ฌ์ฉํ๋ ํจํด๊ณผ ์ต์ ํธ๋ ๋๋ฅผ ์์๋ณผ๊น? ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ํ๋ก์ ํธ๋ฅผ ์งํํ ๋ ์์๋๋ฉด ์ ๋ง ์ ์ฉํ ๊ฑฐ์ผ! ๐ก
1. RESTful API ์ค๊ณ ์์น
๋ฆฌ์์ค ์ค์ฌ ์ค๊ณ
URL์ ๋์ฌ๊ฐ ์๋ ๋ช
์ฌ๋ก ํํํ๊ณ , HTTP ๋ฉ์๋๋ก ํ์๋ฅผ ๋ํ๋ด.
โ ๋์ ์:
- /getUsers
- /createUser
- /updateUser/1
- /deleteUser/1
โ
์ข์ ์:
- GET /users (๋ชฉ๋ก ์กฐํ)
- POST /users (์์ฑ)
- PUT /users/1 (์์ )
- DELETE /users/1 (์ญ์ )
์ ์ ํ HTTP ์ํ ์ฝ๋ ์ฌ์ฉ
- 200 OK: ์ฑ๊ณต
- 201 Created: ์์ฑ ์ฑ๊ณต
- 204 No Content: ์ฑ๊ณตํ์ง๋ง ๋ฐํํ ๋ด์ฉ ์์
- 400 Bad Request: ์๋ชป๋ ์์ฒญ
- 401 Unauthorized: ์ธ์ฆ ํ์
- 403 Forbidden: ๊ถํ ์์
- 404 Not Found: ๋ฆฌ์์ค ์์
- 500 Internal Server Error: ์๋ฒ ์ค๋ฅ
2. DTO (Data Transfer Object) ํจํด
Entity๋ฅผ ์ง์ ํด๋ผ์ด์ธํธ์ ๋ ธ์ถํ์ง ๋ง๊ณ , DTO๋ฅผ ์ฌ์ฉํด์ ํ์ํ ๋ฐ์ดํฐ๋ง ์ ๋ฌํ๋ ๊ฒ ์ข์. ๐
// Entity (๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ๊ณผ ๋งคํ)
@Entity
public class User {
private Long id;
private String username;
private String password; // ๋ฏผ๊ฐํ ์ ๋ณด
private String email;
private LocalDateTime createdDate;
}
// DTO (ํด๋ผ์ด์ธํธ์ ํต์ )
public class UserResponseDto {
private Long id;
private String username;
private String email;
// password๋ ํฌํจํ์ง ์์!
public UserResponseDto(User user) {
this.id = user.getId();
this.username = user.getUsername();
this.email = user.getEmail();
}
}
// Controller์์ ์ฌ์ฉ
@GetMapping("/{id}")
public ResponseEntity<UserResponseDto> getUser(@PathVariable Long id) {
User user = userService.getUser(id);
UserResponseDto dto = new UserResponseDto(user);
return ResponseEntity.ok(dto);
}
๐ก Tip: DTO๋ฅผ ์ฌ์ฉํ๋ฉด ๋น๋ฐ๋ฒํธ ๊ฐ์ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ์ค์๋ก ๋ ธ์ถํ๋ ๊ฑธ ๋ฐฉ์งํ ์ ์์ด. ๋ํ API ๋ฒ์ ์ด ๋ฐ๋์ด๋ Entity๋ ๊ทธ๋๋ก ์ ์งํ๋ฉด์ DTO๋ง ์์ ํ ์ ์์ด์ ์ ์ฐ์ฑ์ด ๋์์ ธ!
3. ์์ธ ์ฒ๋ฆฌ ์ ๋ต
์ ์ญ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ์ปจํธ๋กค๋ฌ์์ ๋ฐ์ํ๋ ์์ธ๋ฅผ ํ ๊ณณ์์ ์ฒ๋ฆฌํ ์ ์์ด. ๐ก๏ธ
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException e) {
ErrorResponse error = ErrorResponse.builder()
.code("USER_NOT_FOUND")
.message(e.getMessage())
.timestamp(LocalDateTime.now())
.build();
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(DuplicateUserException.class)
public ResponseEntity<ErrorResponse> handleDuplicateUser(DuplicateUserException e) {
ErrorResponse error = ErrorResponse.builder()
.code("DUPLICATE_USER")
.message(e.getMessage())
.timestamp(LocalDateTime.now())
.build();
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationError(
MethodArgumentNotValidException e) {
List<String> errors = e.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
ErrorResponse error = ErrorResponse.builder()
.code("VALIDATION_ERROR")
.message("์
๋ ฅ๊ฐ ๊ฒ์ฆ ์คํจ")
.errors(errors)
.timestamp(LocalDateTime.now())
.build();
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneralError(Exception e) {
ErrorResponse error = ErrorResponse.builder()
.code("INTERNAL_ERROR")
.message("์๋ฒ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.")
.timestamp(LocalDateTime.now())
.build();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
4. ์ ํจ์ฑ ๊ฒ์ฆ (Validation)
Bean Validation์ ์ฌ์ฉํ๋ฉด ์ ๋ ฅ๊ฐ ๊ฒ์ฆ์ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ด. โ
public class UserCreateDto {
@NotBlank(message = "์ฌ์ฉ์๋ช
์ ํ์์
๋๋ค.")
@Size(min = 3, max = 20, message = "์ฌ์ฉ์๋ช
์ 3~20์์ฌ์ผ ํฉ๋๋ค.")
@Pattern(regexp = "^[a-zA-Z0-9_]+$",
message = "์ฌ์ฉ์๋ช
์ ์๋ฌธ, ์ซ์, ์ธ๋์ค์ฝ์ด๋ง ๊ฐ๋ฅํฉ๋๋ค.")
private String username;
@NotBlank(message = "๋น๋ฐ๋ฒํธ๋ ํ์์
๋๋ค.")
@Size(min = 8, message = "๋น๋ฐ๋ฒํธ๋ ์ต์ 8์ ์ด์์ด์ด์ผ ํฉ๋๋ค.")
private String password;
@NotBlank(message = "์ด๋ฉ์ผ์ ํ์์
๋๋ค.")
@Email(message = "์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋๋๋ค.")
private String email;
@Min(value = 0, message = "๋์ด๋ 0 ์ด์์ด์ด์ผ ํฉ๋๋ค.")
@Max(value = 150, message = "๋์ด๋ 150 ์ดํ์ฌ์ผ ํฉ๋๋ค.")
private Integer age;
}
// Controller์์ ์ฌ์ฉ
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateDto dto) {
// @Valid ์ด๋
ธํ
์ด์
์ด ์๋์ผ๋ก ๊ฒ์ฆ์ ์ํ
User user = userService.createUser(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
5. ํ์ด์ง๊ณผ ์ ๋ ฌ
๋๋์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋๋ ํ์ด์ง์ด ํ์์ผ. Spring Data JPA๋ ํ์ด์ง์ ์์ฃผ ์ฝ๊ฒ ๊ตฌํํ ์ ์๊ฒ ํด์ค. ๐
// Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Page<User> findByUsernameContaining(String keyword, Pageable pageable);
}
// Service
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public Page<User> searchUsers(String keyword, int page, int size, String sortBy) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy).descending());
return userRepository.findByUsernameContaining(keyword, pageable);
}
}
// Controller
@GetMapping("/search")
public ResponseEntity<Page<UserResponseDto>> searchUsers(
@RequestParam String keyword,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "createdDate") String sortBy) {
Page<User> users = userService.searchUsers(keyword, page, size, sortBy);
Page<UserResponseDto> dtos = users.map(UserResponseDto::new);
return ResponseEntity.ok(dtos);
}
์ด๋ ๊ฒ ํ๋ฉด ํด๋ผ์ด์ธํธ๋ ๋ค์๊ณผ ๊ฐ์ ์์ฒญ์ผ๋ก ํ์ด์ง๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ์ ์์ด:
GET /api/users/search?keyword=kim&page=0&size=10&sortBy=createdDate
6. ๋ณด์ (Spring Security)
์ค๋ฌด์์๋ ๋ณด์์ด ์ ๋ง ์ค์ํด. Spring Security๋ฅผ ์ฌ์ฉํ๋ฉด ์ธ์ฆ๊ณผ ๊ถํ ๊ด๋ฆฌ๋ฅผ ์ฝ๊ฒ ๊ตฌํํ ์ ์์ด. ๐
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
7. ํ ์คํธ ์์ฑ
์ข์ ๊ฐ๋ฐ์๋ ํ ์คํธ๋ฅผ ์์ฑํด. Spring Boot๋ ํ ์คํธ ์์ฑ์ ์ํ ๋ค์ํ ๋๊ตฌ๋ฅผ ์ ๊ณตํด. ๐งช
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private UserService userService;
@Test
@DisplayName("์ฌ์ฉ์ ์์ฑ ํ
์คํธ")
void createUser() throws Exception {
// Given
UserCreateDto dto = new UserCreateDto();
dto.setUsername("testuser");
dto.setPassword("password123");
dto.setEmail("test@example.com");
User user = new User();
user.setId(1L);
user.setUsername(dto.getUsername());
user.setEmail(dto.getEmail());
when(userService.createUser(any())).thenReturn(user);
// When & Then
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(dto)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.username").value("testuser"))
.andExpect(jsonPath("$.email").value("test@example.com"));
}
@Test
@DisplayName("์ฌ์ฉ์ ์กฐํ ํ
์คํธ")
void getUser() throws Exception {
// Given
User user = new User();
user.setId(1L);
user.setUsername("testuser");
user.setEmail("test@example.com");
when(userService.getUser(1L)).thenReturn(user);
// When & Then
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username").value("testuser"));
}
}
๐ ํ์ต ๋ก๋๋งต๊ณผ ์ถ์ฒ ์๋ฃ
์๋ฐ ์น ๊ฐ๋ฐ์ ์ฒ์ ์์ํ๋ ์ฌ๋๋ค์ ์ํ ํ์ต ์์๋ฅผ ์ ๋ฆฌํด๋ณผ๊ฒ. ๐
์ถ์ฒ ํ์ต ์๋ฃ
๐ ์ฑ
1. ์ด๊ธ
- "Head First Servlets and JSP" - ๊ทธ๋ฆผ์ผ๋ก ์ฝ๊ฒ ๋ฐฐ์ฐ๋ ์๋ธ๋ฆฟ๊ณผ JSP
- "์๋ฐ ์น ํ๋ก๊ทธ๋๋ฐ Next Step" - ์ค์ต ์ค์ฌ์ ์
๋ฌธ์
2. ์ค๊ธ
- "ํ ๋น์ ์คํ๋ง 3.1" - Spring์ ๋ฐ์ด๋ธ
- "์คํ๋ง ๋ถํธ์ AWS๋ก ํผ์ ๊ตฌํํ๋ ์น ์๋น์ค" - ์ค์ ํ๋ก์ ํธ
3. ๊ณ ๊ธ
- "์คํ๋ง ๋ง์ดํฌ๋ก์๋น์ค ์ฝ๋ฉ ๊ณต์์" - MSA ์ํคํ
์ฒ
- "์๋ฐ ORM ํ์ค JPA ํ๋ก๊ทธ๋๋ฐ" - JPA ์๋ฒฝ ๊ฐ์ด๋
๐ ์จ๋ผ์ธ ๊ฐ์
- ์ธํ๋ฐ: "์คํ๋ง ์
๋ฌธ", "์คํ๋ง ํต์ฌ ์๋ฆฌ" (๊น์ํ)
- ์ ๋ฐ๋ฏธ: "Spring Framework Master Class"
- ๋ฐฑ๊ธฐ์ ์ ์คํ๋ง ๋ถํธ ์๋ฆฌ์ฆ
- ์ํ์ฝ๋ฉ: ์๋ธ๋ฆฟ๊ณผ JSP ๊ธฐ์ด
๐ป ์ค์ต ํ๋ก์ ํธ ์์ด๋์ด
1. ๊ฒ์ํ ์์คํ
- ํ์๊ฐ์
/๋ก๊ทธ์ธ
- CRUD ๊ธฐ๋ฅ
- ํ์ด์ง, ๊ฒ์
- ํ์ผ ์
๋ก๋
2. ์ผํ๋ชฐ
- ์ํ ๊ด๋ฆฌ
- ์ฅ๋ฐ๊ตฌ๋
- ์ฃผ๋ฌธ/๊ฒฐ์
- ๋ฆฌ๋ทฐ ์์คํ
3. ๋ธ๋ก๊ทธ ํ๋ซํผ
- ๋งํฌ๋ค์ด ์๋ํฐ
- ํ๊ทธ ์์คํ
- ๋๊ธ ๊ธฐ๋ฅ
- RSS ํผ๋
4. TODO ๊ด๋ฆฌ ์ฑ
- RESTful API
- ์ค์๊ฐ ์๋ฆผ
- ํ์
๊ธฐ๋ฅ
- ํต๊ณ ๋์๋ณด๋
๐ ์ค๋ฌด์์์ ํ์ฉ
์ด์ ์ค๋ฌด์์ ์ด๋ป๊ฒ ํ์ฉ๋๋์ง ์์๋ณผ๊น? ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ํ๋ก์ ํธ๋ฅผ ์งํํ ๋ ์ฐธ๊ณ ํ๋ฉด ์ข์ ๊ฑฐ์ผ! ๐ผ
ํ๋์ ์ธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ํคํ ์ฒ
์์ฆ ์ค๋ฌด์์๋ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๋ง์ด ์ฌ์ฉํด:
ํ๋ก ํธ์๋ (React, Vue, Angular)
โ REST API
๋ฐฑ์๋ (Spring Boot)
โ
์๋น์ค ๊ณ์ธต (๋น์ฆ๋์ค ๋ก์ง)
โ
๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต (JPA, MyBatis)
โ
๋ฐ์ดํฐ๋ฒ ์ด์ค (MySQL, PostgreSQL)
๊ณ์ธต๋ณ ์ญํ
Controller ๊ณ์ธต
- HTTP ์์ฒญ/์๋ต ์ฒ๋ฆฌ
- ์
๋ ฅ๊ฐ ๊ฒ์ฆ
- ์ธ์ฆ/๊ถํ ์ฒดํฌ
- ์์ธ ์ฒ๋ฆฌ
Service ๊ณ์ธต
- ๋น์ฆ๋์ค ๋ก์ง ๊ตฌํ
- ํธ๋์ญ์
๊ด๋ฆฌ
- ์ฌ๋ฌ Repository ์กฐํฉ
- ๋๋ฉ์ธ ๊ท์น ์ ์ฉ
Repository ๊ณ์ธต
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ
- CRUD ์ฐ์ฐ
- ์ฟผ๋ฆฌ ์คํ
- ๋ฐ์ดํฐ ๋ณํ
์ฑ๋ฅ ์ต์ ํ ํ
1. N+1 ๋ฌธ์ ํด๊ฒฐ
JPA๋ฅผ ์ฌ์ฉํ ๋ ๊ฐ์ฅ ํํ ์ฑ๋ฅ ๋ฌธ์ ์ผ. Fetch Join์ด๋ Entity Graph๋ฅผ ์ฌ์ฉํด์ ํด๊ฒฐํ ์ ์์ด.
2. ์บ์ฑ ํ์ฉ
์์ฃผ ์กฐํ๋๋ ๋ฐ์ดํฐ๋ Redis๋ Spring Cache๋ฅผ ์ฌ์ฉํด์ ์บ์ฑํ๋ฉด ์ฑ๋ฅ์ด ํฌ๊ฒ ํฅ์๋ผ.
3. ๋น๋๊ธฐ ์ฒ๋ฆฌ
์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ @Async๋ฅผ ์ฌ์ฉํด์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ์ข์์ ธ.
4. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ๋ฑ์ค
๊ฒ์์ด ์์ฃผ ์ผ์ด๋๋ ์ปฌ๋ผ์๋ ์ธ๋ฑ์ค๋ฅผ ์์ฑํด์ ์กฐํ ์๋๋ฅผ ๋์ฌ.
5. ํ์ด์ง ์ฒ๋ฆฌ
๋๋์ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ์กฐํํ์ง ๋ง๊ณ ํ์ด์ง์ ์ ์ฉํด์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ฌ.
๋ณด์ ์ฒดํฌ๋ฆฌ์คํธ
โ
SQL Injection ๋ฐฉ์ง (PreparedStatement ์ฌ์ฉ)
โ
XSS ๊ณต๊ฒฉ ๋ฐฉ์ง (์
๋ ฅ๊ฐ ์ด์ค์ผ์ดํ ์ฒ๋ฆฌ)
โ
CSRF ํ ํฐ ์ฌ์ฉ
โ
๋น๋ฐ๋ฒํธ ์ํธํ (BCrypt)
โ
HTTPS ์ฌ์ฉ
โ
JWT ํ ํฐ ๋ง๋ฃ ์๊ฐ ์ค์
โ
๋ฏผ๊ฐํ ์ ๋ณด ๋ก๊น
๊ธ์ง
โ
๊ถํ ์ฒดํฌ (๋ฉ์๋ ๋ ๋ฒจ)
โ
ํ์ผ ์
๋ก๋ ๊ฒ์ฆ
โ
Rate Limiting ์ ์ฉ
๐ฏ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์, ์ ๋ง ๊ธด ์ฌ์ ์ด์์ง? ๐
Servlet๋ถํฐ ์์ํด์ JSP, MVC ํจํด, Spring MVC, ๊ทธ๋ฆฌ๊ณ Spring Boot๊น์ง ์๋ฐ ์น ๊ฐ๋ฐ์ ์ ์ฒด ํ๋ฆ์ ์ดํด๋ดค์ด.
์ฒ์์๋ ๋ณต์กํด ๋ณด์ผ ์ ์์ง๋ง, ํ๋์ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ฐฐ์๊ฐ๋ค ๋ณด๋ฉด ์ด๋์ ์ค๋ฌด์์ ํ์ฉํ ์ ์๋ ์์ค์ด ๋ ๊ฑฐ์ผ. ์ค์ํ ๊ฑด ๊พธ์คํ ์ค์ตํ๊ณ , ์์ ํ๋ก์ ํธ๋ผ๋ ์ง์ ๋ง๋ค์ด๋ณด๋ ๊ฑฐ์ผ. ๐ช
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ Spring Boot๋ฅผ ํ์ฉํ ๋ค์ํ ํ๋ก์ ํธ๋ค์ด ์งํ๋๊ณ ์์ด. ๊ธฐ์ด๋ฅผ ํํํ ๋ค์ง๊ณ ๋๋ฉด, ์ค์ ํ๋ก์ ํธ์ ์ฐธ์ฌํด์ ๊ฒฝํ์ ์๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ์ค๋ฌด ๊ฒฝํ์ ํตํด ๋ฐฐ์ฐ๋ ๊ฒ ๊ฐ์ฅ ๋น ๋ฅด๊ณ ํจ๊ณผ์ ์ด๊ฑฐ๋ ! ๐
์๋ฐ ์น ๊ฐ๋ฐ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. Spring 6, Spring Boot 3, Virtual Threads, GraalVM ๊ฐ์ ์๋ก์ด ๊ธฐ์ ๋ค์ด ๊ณ์ ๋์ค๊ณ ์์ง. ํ์ง๋ง ๊ธฐ๋ณธ ์๋ฆฌ๋ ๋ณํ์ง ์์. Servlet, JSP, MVC ํจํด ๊ฐ์ ๊ธฐ์ด๋ฅผ ์ ์ดํดํ๊ณ ์์ผ๋ฉด, ์๋ก์ด ๊ธฐ์ ๋ ์ฝ๊ฒ ๋ฐฐ์ธ ์ ์์ด.
์ด ๊ธ์ด ์๋ฐ ์น ๊ฐ๋ฐ์ ์์ํ๋ ๋ฐ ๋์์ด ๋์์ผ๋ฉด ์ข๊ฒ ์ด. ๊ถ๊ธํ ์ ์ด ์๊ฑฐ๋ ๋ ์๊ณ ์ถ์ ๋ด์ฉ์ด ์๋ค๋ฉด, ์ฌ๋ฅ๋ท์ ์ง์์ธ์ ์ฒ์์ ๋ค๋ฅธ ๊ธ๋ค๋ ์ฐพ์๋ณด๊ณ , ์ ๋ฌธ๊ฐ๋ค๊ณผ ์ํตํด๋ณด๋ ๊ฒ๋ ์ถ์ฒํด! ๐ณ
์, ์ด์ ์ฝ๋ฉ์ ์์ํด๋ณผ๊น? ํ์ดํ
! ๐
โจ Happy Coding! โจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.