커스텀 태그(JSTL) 라이브러리 사용, (EL 표현식)

목차

    1. JSTL

    JSTL 이란?

    JSTL (JavaServer Pages Standard Tag Library)는 JSP에서 자주 사용하는 기능들을 쉽게 사용할 수 있도록 미리 정의된 태그 라이브러리이다. JSTL은 표현 언어(EL), 조건 처리, 반복 처리, 포맷팅, XML 처리 등을 위한 태그를 제공한다. 이를 통해 JSP 코드의 가독성을 높이고, 비즈니스 로직을 JSP에서 분리할 수 있다.

    JSTL 라이브러리 종류

    1. Core Tags

    : JSTL의 Core 태그 라이브러리는 가장 기본적인 조건문, 반복문, 변수 지원 등을 포함한다. 주로 데이터 조작 및 제어 흐름을 처리하는 데 사용된다.

    • <c:if> : 조건문을 처리하는 태그.
    • <c:choose>, <c:when>, <c:otherwise> : 다중 조건문을 처리하는 태그.
    • <c:forEach> : 반복문을 처리하는 태그.
    • <c:set> : 변수 값을 설정하는 태그.
    • <c:remove> : 변수를 제거하는 태그.
    • <c:catch> : 예외를 처리하는 태그.

    2. Formatting Tags

    : Formatting 태그 라이브러리는 날짜, 숫자 등을 포맷팅하는 데 사용된다. 국제화를 지원하는 포맷팅 기능을 제공한다.

    3. SQL Tags

    : SQL 태그 라이브러리는 JSP에서 직접 데이터베이스 작업을 수행할 수 있도록 도와준다.

    • <sql:query> : 데이터베이스에 쿼리를 실행하는 태그.
    • <sql:update> : 데이터베이스에 업데이트를 수행하는 태그.
    • <sql:param> : 쿼리에 매개변수를 설정하는 태그.
    • <sql:setDataSource> : 데이터베이스 연결을 설정하는 태그.

    4. XML Tags

    : XML 태그 라이브러리는 XML 문서를 처리하는 데 사용된다. XPath를 사용하여 XML 데이터를 탐색하고 조작할 수 있다.

    • <x:parse>: XML 문서를 파싱하는 태그.
    • <x:out>: XML 문서의 값을 출력하는 태그.
    • <x:forEach>: XML 문서의 각 노드를 반복하는 태그.
    • <x:set>: XPath 표현식을 설정하는 태그.

    2. 라이브러리 설정

    라이브러리 설정 방법 (톰캣 10.x.x 환경)

    1. 라이브러리 파일 다운로드: 위에서 제공한 링크에서 JAR 파일을 다운로드.
      • jakarta.servlet.jsp.jstl-api-3.0.0.jar
      • jakarta.servlet.jsp.jstl-3.0.0.jar
    2. JAR 파일 추가:
      • 프로젝트의 WEB-INF/lib 디렉터리에 다운로드한 두 JAR 파일을 복사.

    자세한 내용은 https://kyeong-hoon.tistory.com/312 참고

    연습 코드

    <%@page import="java.util.Date"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
    
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>JSTL 예제</title>
    </head>
    <body>
    	<h1>JSTL 예제</h1>
    	<% // 스크립트 릿 대신에 <c:set....> 태그를 사용할 수 있다. %>
    	<c:set var="message" value="JSTL ~~~ "/>
    	<p>메시지 : </p><c:out value="${message}" />
    	
    	<!-- IF -->
    	<c:if test="${message != null}">
    		<p>메시지 값이 null 아닙니다</p>
    	</c:if>
    	
    	<!-- foreach  -->
    	<c:forEach var="i" begin="1" end="10" >
    		<p>Number : ${i}</p>	
    	</c:forEach>
    	
    	<h2>JSTL Formatting Example</h2>
    	<c:set var="now" value="<%=new Date() %>"/>
    	<fmt:formatDate var="formattedDate"  value="${now}" pattern="yyyy-MM-dd HH:mm:ss" />
    	
    	<p>현재 시간 : ${formattedDate} </p>
    	
    	<c:set var="price" value="12345.678"/>
    	<fmt:formatNumber value="${price}" type="currency" var="formatPrice" />
    	<p>Format Price : ${formatPrice} </p>
    
    </body>
    </html>

    3. 예제 코드

    User 클래스 생성(DTO)
    package com.tenco.models;
    
    import lombok.Data;
    
    @Data
    public class User {
    	
    	private int id;
    	private String username;
    	private String password;
    	private String email;
    }
    UserDAOImpl 생성
    package com.tenco.repository;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    import com.tenco.models.User;
    
    /**
     * 인터페이스 설계 생략.
     */
    public class UserDAOImpl {
    
    	private static final String URL = "jdbc:mysql://localhost:3306/m_todo?serverTimezone=Asia/Seoul";
    	private static final String USER = "root";
    	private static final String PASSWORD = "asd123";
    
    	public UserDAOImpl() {
    		try {
    			Class.forName("com.mysql.cj.jdbc.Driver");
    
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    
    	private Connection getConnection() throws SQLException {
    		return DriverManager.getConnection(URL, USER, PASSWORD);
    	}
    
    	public List<User> getUsers() {
    		List<User> userList = new ArrayList<>();
    
    		String sql = " SELECT * from users ";
    
    		try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery()) {
    
    			while (rs.next()) {
    				User user = new User();
    				user.setId(rs.getInt("id"));
    				user.setUsername(rs.getString("username"));
    				user.setEmail(rs.getString("email"));
    				userList.add(user);
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    
    		return userList;
    	}
    }
    < UserDAOImpl 클래스가 인터페이스 설계를 포함하지 않은 경우,
       SOLID 원칙 중 다음 원칙들이 위배될 사항들은 뭐가 있을까?   >

    1. 단일 책임 원칙 (Single Responsibility Principle, SRP)
    UserDAOImpl 클래스가 인터페이스를 포함하지 않으면, 이 클래스는 데이터베이스와 직접적으로 연결되고, 데이터를 조작하는 역할을 모두 수행 함. 이는 데이터 접근 로직과 비즈니스 로직이 혼합될 가능성이 높아, 클래스가 여러 가지 책임을 지게 됨.

    2. 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
    ISP는 클라이언트가 자신이 사용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리 UserDAO가 인터페이스를 사용하지 않으면, 모든 클라이언트는 필요하지 않은 메서드를 사용할 수밖에 없게 되어 인터페이스가 분리되지 않은 상태가 됨.

    3. 의존성 역전 원칙 (Dependency Inversion Principle, DIP)
    DIP는 고수준 모듈이 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 함 이 클래스는 구체적인 구현에 의존하게 되어 DIP를 위배하게 됨.

    4. 기존 프로젝트를 jstl로 변환하기

    signIn.jsp
    <div class="container">
        <h2>회원가입</h2>
        <!-- 메세지 출력 -->
        <%
            String message = (String) request.getParameter("message");
            if (message != null) {
                if (message.equalsIgnoreCase("invalid")) {
        %>
            <p class="message" style="color: red;">로그인 오류</p>
        <% 
                } else if (message.equalsIgnoreCase("success")) { 
        %>
            <p class="message" style="color: blue;"><%=message %></p>
        <% 
                }
            } 
        %>
        <form action="/mvc/user/signIn" method="post">
            <label for="username">사용자 이름</label>
            <input type="text" name="username" id="username" value="야스오1">
            <label for="password">비밀번호</label>
            <input type="password" name="password" id="password" value="1234">
            <button type="submit">로그인</button>
        </form>
    </div>

     

    변경후

    <div class="container">
        <h2>로그인</h2>
        <!-- 메세지 출력 -->
        <c:if test="${param.message != null}">
            <c:choose>
                <c:when test="${param.message == 'invalid'}">
                    <p class="message" style="color: red;">로그인 오류</p>
                </c:when>
                <c:when test="${param.message == 'success'}">
                    <p class="message" style="color: blue;">
                        <c:out value="${param.message}" />
                    </p>
                </c:when>
            </c:choose>
        </c:if>
    
        <form action="/mvc/user/signIn" method="post">
            <label for="username">사용자 이름</label>
            <input type="text" name="username" id="username" value="야스오1">
            <label for="password">비밀번호</label>
            <input type="password" name="password" id="password" value="1234">
            <button type="submit">로그인</button>
        </form>
    </div>
    signUp.jsp
    <%
        String message = (String) request.getParameter("message");
        if (message != null) {
    %>
        <p style="color: red;"><%= message %></p>
    <% } %>
    <c:if test="${message != null}">
        <p style="color: red;">
            <c:out value="${message}"></c:out>
        </p>
    </c:if>
    todoDetail.jsp
    <%
        TodoDTO todo = (TodoDTO) request.getAttribute("todo");
        if (todo != null) {
    %>
    <h1>상세 페이지</h1>
    <p> 제목 : <%=todo.getTitle() %> </p><br>
    <p> 설명 : <%=todo.getDescription() %> </p><br>
    <p> 마감일 : <%=todo.getDueDate() %> </p><br>
    <p> 완료여부 : <%=todo.isCompleted() ? "완료" : "미완료" %> </p><br>
    <hr><br>
    <form action="update" method="post">
        <input type="hidden" name="id" value="<%= todo.getId() %>">
        <label for="title">제목 : </label>
        <input type="text" name="title" id="title" value="<%=todo.getTitle() %>">
        <br><br>
        <label for="description">설명 : </label>
        <textarea rows="30" cols="50" name="description" id="description">
            <%=todo.getDescription() %>
        </textarea>
        <br><br>
        <label for="dueDate">마감일 : </label>
        <input type="date" name="dueDate" id="dueDate" value="<%=todo.getDueDate() %>">
        <br><br>
        <label for="completed">완료여부 : </label>
        <input type="checkbox" name="completed" id="completed" <%= todo.isCompleted() ? "checked" : "" %> >
        <br>
        <button type="submit">수정</button>
        <br>
    </form>
    
    <a href="todoForm"> 새 할일 추가</a>
    <br><br>
    <a href="list">목록으로 돌아가기</a>
    <br><br>
    <% } else { %>	
        <p> 잘못된 요청입니다 </p>
        <a href="list">목록으로 돌아가기</a>
    <% } %>
    <c:choose>
        <c:when test="${todo != null }">
            <h1>상세 페이지</h1>
            <p> 제목 : <c:out value="${todo.title}" /></p>
            <br>
            <p> 설명 : <c:out value="${todo.description}" /></p>
            <br>
            <p> 마감일 : <c:out value="${todo.dueDate}" /></p>
            <br>
            <p> 완료여부 : <c:out value="${todo.completed ? '완료' : '미완료'}" /></p>
            <br><hr><br>
        <form action="update" method="post">
            <input type="hidden" name="id" value="${todo.id}">
            <label for="title">제목 : </label>
            <input type="text" name="title" id="title" value="${todo.title}">
            <br><br>
            <label for="description">설명 : </label>
            <textarea rows="30" cols="50" name="description" id="description">
        		<c:out value="${todo.description}"/>
    		</textarea>
            <br> <br> 
            <label for="dueDate">마감일 : </label>
            <input type="date" name="dueDate" id="dueDate" value="${todo.dueDate}">
            <br> <br>
            <label for="completed">완료여부 : </label>
            <input type="checkbox" name="completed" id="completed" ${todo.completed ? 'completed' : ''}>
            <br>
            <button type="submit">수정</button>
            <br>
        </form>
    
        <a href="todoForm"> 새 할일 추가</a>
        <br> <br>
        <a href="list">목록으로 돌아가기</a>
        <br> <br>
        </c:when>
        <c:otherwise>
            <p>잘못된 요청입니다</p>
            <a href="list">목록으로 돌아가기</a>
        </c:otherwise>
    </c:choose>
    todoList.jsp
    <%
        // 샘플 데이터
        List<TodoDTO> todoList = (List<TodoDTO>) request.getAttribute("list");
    
        if (todoList != null && !todoList.isEmpty()) {
    %>
    <h2>할 일 목록</h2>
    <a href="todoForm"> 새 할일 추가</a>
    <br><br>
    <table border="1">
        <tr>
            <th>제목</th>
            <th>설명</th>
            <th>마감일</th>
            <th>완료 여부</th>
            <th>(액션 - 버튼)</th>
        </tr>
        <% for (TodoDTO todo : todoList) { %>
        <tr>
            <td><%=todo.getTitle() %></td>
            <td><%=todo.getDescription() %></td>
            <td><%=todo.getDueDate() %></td>
            <td><%=todo.isCompleted() ? "완료" : "미완료" %></td>
            <td>
                <a href="detail?id=<%= todo.getId() %>">상세보기</a>
                <form action="delete" method="get">
                <input type="hidden" name="id" value="<%= todo.getId() %>"> 
                    <button type="submit">삭제</button>
                </form>
            </td>
        </tr>
        <% } %>
    </table>
    <% } else { %>
    <p>등록된 할 일이 없습니다.</p>
    <% } %>
    <c:choose>
        <c:when test="${list != null && not empty list }">
    <h2>할 일 목록</h2>
    <a href="todoForm"> 새 할일 추가</a>
    <br>
    <br>
    <table border="1">
        <tr>
            <th>제목</th>
            <th>설명</th>
            <th>마감일</th>
            <th>완료 여부</th>
            <th>(액션 - 버튼)</th>
        </tr>
        <c:forEach var="todo" items="${list}">
        <tr>
            <td><c:out value="${todo.title}"/></td>
            <td><c:out value="${todo.description}"/></td>
            <td><c:out value="${todo.dueDate}"/></td>
            <td><c:out value="${todo.completed ? '완료' : '미완료'}"/></td>
            <td>
                <a href="detail?id=${todo.id}">상세보기</a>
                <form action="delete" method="get">
                    <input type="hidden" name="id" value="${todo.id}">
                    <button type="submit">삭제</button>
                </form>
            </td>
        </tr>
        </c:forEach>
    </table>
        </c:when>
        <c:otherwise>
    <p>등록된 할 일이 없습니다.</p>
        </c:otherwise>
    </c:choose>

    JSP 목차로 돌아가기

     

    'Java > JSP' 카테고리의 다른 글

    JSP 목차  (0) 2024.07.15
    JSTL 을 활용한 게시판 기능 만들기  (0) 2024.07.15
    JSP와 MVC 패턴 Todo 프로젝트  (0) 2024.07.09
    JSP(교재) 웹 프로그래밍 기초  (0) 2024.07.09
    간단한 게시판 만들어 보기  (0) 2024.07.08