Mybatis의 기능으로 DB를 매우 간편하게 조회할 수 있는데


다음의 로그인 예제를 컨트롤러부터 보고 확인해보자.


순서는 controller -> Service -> dao




String userId = request.getParameter("userId");
        String userPwd = request.getParameter("userPwd");
       
        System.out.println("userId : " + userId);
        System.out.println("userPwd : " + userPwd);
       
        // myBatis는 데이터 한개만 받기 때문에 처리를 해야함
        Member m = new Member();
        m.setUserId(userId);
        m.setUserPwd(userPwd);
       
        // 객체간의 의존성을 줄여줌, Spring의 핵심기능
        // 예) Service에서 메소드를 수정하면 Controller에서도 수정을 해야함 -> 의존성이 강하다.
        MemberService ms = new MemberServiceImpl();
        try {
            Member member = ms.selectMember(m);
           
            HttpSession session = request.getSession();
           
            session.setAttribute("loginUser", member);
           
            response.sendRedirect("index.jsp");
        } catch (LoginFailException e) {
            RequestDispatcher error = request.getRequestDispatcher("WEB-INF/views/common/errorPage.jsp");
            request.setAttribute("message", e.getMessage());
           
            error.forward(request, response);
        }



컨트롤러에서 Service의 인터페이스 생성, 일반 클래스 객체화 방법을 통해서 호출


JSP 페이지로부터 받은 값 m을 전달한다.








public interface MemberService {
    // 로그인용 메소드
    // public abstract가 전부 들어가기 때문에 애초에 생략해도 된다.(Interface)
    Member selectMember(Member m) throws LoginFailException;
   
}



import org.apache.ibatis.session.SqlSession;

import com.kh.mb.member.model.dao.MemberDao;
import com.kh.mb.member.model.exception.InsertFailException;
import com.kh.mb.member.model.exception.LoginFailException;
import com.kh.mb.member.model.vo.Member;
import static com.kh.mb.common.Template.*;


public class MemberServiceImpl implements MemberService{
   
    // 자동으로 더 넓거나 같은 접근제한자가 잡힌다.
    // 이러한 인터페이스를 사용한 구조는 딱 정해져있는 메소드를 사용하므로(매개변수도 바꿀 수 없다.)
    // 행위를 강제한다고 한다.
    @Override
    public Member selectMember(Member m) throws LoginFailException {
        // SqlSession은 MyBatis에서 커넥션 대신 사용
        SqlSession session = getSqlSession();
       
        Member member = new MemberDao().selectMember(session, m);
       
        session.close();
       
        return member;
    }

}


인터페이스를 이용한 구조로 강제와된 selectMember 메소드드를 통해 Dao에 값을 전달


SqlSession session = getSqlSession();의 SqlSession 객체는 myBatis 라이브러리의 객체이다.


getSqlSession은 myBatis의 SQLSession을 생성하기 위해 공통적으로 이용하는 기능이므로


따로 common에 Template 자바 파일을 만들어 생성/관리 한다.


해당 템플릿 파일은 다음과 같다.




import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Template {
    public static SqlSession getSqlSession() {
        SqlSession session = null;
       
        try {
            InputStream stream = Resources.getResourceAsStream("resources/mybatis-config.xml");
           
            System.out.println(stream);
           
            // openSession 인자에 false를 주면 Auto Commit이 차단 됨
            session = new SqlSessionFactoryBuilder().build(stream).openSession(false);
           
            System.out.println(session);
           
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
       
       
        return session;
    }
}



Resources.getResourceAsStream("resources/mybatis-config.xml"); 를 통해서 해당 경로에 있는


mybatis-config.xml에 작성 해 둔 설정을 불러와 SqlSession 객체를 생성한다.


mybatis-config.xml 파일은 다음과 같이 쓰여져있다.


<?xml version="1.0" encoding="UTF-8"?>

<!-- 태그명-내용 = 키-값 처럼 쓰인다. -->

<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <settings>
        <!-- 만약에 null로 데이터가 전달 되었다면 빈칸이 아닌 null로 인식해라 라는 뜻 -->
        <setting name="jdbcTypeForNull" value="NULL"/>
    </settings>
   
    <!-- vo 객체들의 풀 네임을 사용하기 번거롭기 때문에 별칭을 등록하는 부분 -->
    <typeAliases>
        <!-- 클래스에 대한 별칭 -->
        <typeAlias type="com.kh.mb.member.model.vo.Member" alias="Member"/>
        <typeAlias type="com.kh.mb.board.model.vo.Board" alias="Board"/>
        <typeAlias type="com.kh.mb.board.model.vo.Reply" alias="Reply"/>
    </typeAliases>
   
    <!-- DB 연결할 설정에 대한 정보를 선언하는 부분 -->
    <environments default="firstDev">
        <environment id="firstDev">
            <!-- 트랙잭션 매니저는 JDBC 혹은 MANAGED 둘 중 하나를 선택할 수 있음 -->
            <!-- JDBC는 JDBC가 commit과 rollback의 기능을 직접 사용 가능하게 하는 옵션(수동 commit) -->
            <!-- MANAGED는 트랙잭션에 대해 어떤 영향도 행사하지 않는다는 뜻(자동 commit) -->
            <transactionManager type="JDBC"/>
           
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"></property>
                <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
                <property name="username" value="mybatis"/>
                <property name="password" value="mybatis"/>
            </dataSource>
        </environment>
    </environments>
   
    <mappers>
        <mapper resource="resources/mappers/member-mapper.xml"/>
        <mapper resource="resources/mappers/board-mapper.xml"/>
    </mappers>
   
</configuration>






<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">


이 부분은 mybatis홈페이지에서 가져와야 한다.


http://www.mybatis.org/mybatis-3/ko/getting-started.html


위 링크에 가면 자세히 설명이 되어있다


mybatis의 경우 페이지 한글화가 정말 잘 되 있어서


위의 페이지에서 xml 태그들의 설명을 위의 예제와 함께 이해해보자.








예제로 돌아와서 서비스에서


Member member = new MemberDao().selectMember(session, m);으로


SqlSession객체와 로그인 정보를 담고있는 멤버 객체를 Dao에 전달한다.





import org.apache.ibatis.session.SqlSession;

import com.kh.mb.member.model.exception.InsertFailException;
import com.kh.mb.member.model.exception.LoginFailException;
import com.kh.mb.member.model.vo.Member;

public class MemberDao{
   
    public Member selectMember(SqlSession session, Member m) throws LoginFailException {
        Member member = null;
       
        member = session.selectOne("Member.loginMember", m);
       
        if (member == null) {
            session.close();
            throw new LoginFailException("로그인 실패");
           
        }
       
        return member;
    }
}


selectMember 메소드에서 m에 담겨있는 정보를


selectOne이라는 mybatis 메소드를 이용하게 된다.


Member.loginMember 이라는 키워드를 통해서


Member라는 해당 namespace로 지정된 xml 파일을 찾아 loginMember에 지정된 쿼리문을 실행시킨다.


그 쿼리문에는 Member 객체를 받아서 이용하게 된다.







먼저 아까 SqlSession에 설정한 mybatis-config xml 파일을 살펴보면


<mappers>
        <mapper resource="resources/mappers/member-mapper.xml"/>
        <mapper resource="resources/mappers/board-mapper.xml"/>
    </mappers>


mappers 태그에 member-mapper.xml이라는 파일을 볼 수 있다.


그리고 member-mapper.xml 파일은 다음과 같이 쓰여져있다.







<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<!-- 파일의 별칭 -->
<mapper namespace="Member">
    <resultMap type="com.kh.mb.member.model.vo.Member" id="memberResultSet">
        <id property="mid" column="MID"/>
        <result property="userId" column="USER_ID"/>
        <result property="userPwd" column="USER_PWD"/>
        <result property="userName" column="USER_NAME"/>
        <result property="email" column="EMAIL"/>
        <result property="birthDay" column="BIRTHDAY"/>
        <result property="gender" column="GENDER"/>
        <result property="phone" column="PHONE"/>
        <result property="address" column="ADDRESS"/>
        <result property="enrollDate" column="ENROLL_DATE"/>
        <result property="modifyDate" column="MODIFY_DATE"/>
        <result property="status" column="STATUS"/>
    </resultMap>
   
    <select id="loginMember" parameterType="Member" resultMap="memberResultSet">
        SELECT *
        FROM MEMBER
        WHERE USER_ID = #{userId}
        AND USER_PWD = #{userPwd}
    </select>
   
    <insert id="insertMember" parameterType="Member">
        INSERT INTO member
        VALUES (SEQ_MID.NEXTVAL, #{userId}, #{userPwd}, #{userName}, #{email}, #{birthDay}, #{gender}, #{phone}, #{address}, SYSDATE, SYSDATE, DEFAULT)
    </insert>
   
   
</mapper>




<mapper namespace="Member">


namespace가 Member로 설정되서


session.selectOne("Member.loginMember", m);라는 메소드에서 이 xml까지 찾아와서


<select id="loginMember" parameterType="Member" resultMap="memberResultSet">
        SELECT *
        FROM MEMBER
        WHERE USER_ID = #{userId}
        AND USER_PWD = #{userPwd}
    </select>


id가 loginMember 라고 지정되어 있는 이 쿼리문을 사용하게 된다.


parameterType과 resultMap, #{userId} 등 특이한 키워드들이 보이는데


이에 대한 설명은 myBatis xml 파일을 설명하는 게시글에서 추가로 한다.


참고로 mybatis 홈페이지에도 충분히 이해하기 쉽게 설명이 되어있다.






xml에 지정해놓은 여러가지 키워드들을 통해서 DB를 조회하고 값을 리턴하게 된다.


























Front Controller는 맵핑 주소를 관리하는 java 파일이 된다.


맵핑 주소로 접근 할 때 모든 접근을 Front Controller를 통하여 해당하는 컨트롤러에 이동시켜준다.


예전에 했던 필터와 유사




예를 들어 주소가 me로 끝나는 맵핑 주소는 항상 위의 컨트롤러를 지나게 된다.


다음 예를 보자.



package com.kh.mb.frontController;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("*.me")
public class MemberFrontController extends HttpServlet {
    private static final long serialVersionUID = 1L;
      
    public MemberFrontController() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
       
        String uri = request.getRequestURI();
        System.out.println(uri);
       
        String action = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf(".me"));
        System.out.println("action : " + action);
       
        RequestDispatcher rd = null;
        switch (action) {
        case "login":
            rd = request.getRequestDispatcher("login");
            break;
        case "logout":
            rd = request.getRequestDispatcher("logout");
            break;
        case "showInsertForm":
            rd = request.getRequestDispatcher("showInsertForm");
            break;
        case "minsert":
            rd = request.getRequestDispatcher("minsert");
            break;
        }
       
        rd.forward(request, response);
       
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}





필터 클래스에서 했던것처럼 문자 형식을 UTF-8로 처리해주고


맵핑 주소 문자열을 분리해서 Switch Case 문으로 해당하는 컨트롤러로 연결시킨다.






















프로젝트를 진행하게 되면 절대 혼자서 하지 않고 여러명에서 잡고 진행하게 된다.


하지만 지역마다 사투리가 있는것 처럼 사람마다 다양한 코딩 스타일이 존재하고


메소드 이름을 짓는 방식이 다르고 등등 여러가지 일치하지 않는 부분이 생기게 된다.


이를 해결하기 위해 핵심 기능 구현부분에 Interface를 적용한다.







Interface를 상속받는 클래스는 Interface에 존재하는 메소드 형식을 사용해야 하는 제약이 걸리기 때문에


틀(Frame)을 어느정도 일치 시킬 수 있다.


한번 확인해보자.



public interface MemberService {
    // 로그인용 메소드
    // public abstract가 전부 들어가기 때문에 애초에 생략해도 된다.(Interface)
    Member selectMember(Member m) throws LoginFailException;

    // 회원 가입용 메소드
    void insertMember(Member m) throws InsertFailException;
   
}


먼저 Member Service라는 인터페이스이고


throw가 붙은 두가지의 메소드가 존재한다.








다음은 MemberServiceImpl 클래스는 인터페이스 MemberService를 상속받는다.



public class MemberServiceImpl implements MemberService{
   
    // 자동으로 더 넓거나 같은 접근제한자가 잡힌다.
    // 이러한 인터페이스를 사용한 구조는 딱 정해져있는 메소드를 사용하므로(매개변수도 바꿀 수 없다.)
    // 행위를 강제한다고 한다.
    @Override
    public Member selectMember(Member m) throws LoginFailException {
        // SqlSession은 MyBatis에서 커넥션 대신 사용
        SqlSession session = getSqlSession();
       
        Member member = new MemberDao().selectMember(session, m);
       
        session.close();
       
        return member;
    }

    // 회원 가입용 메소드
    @Override
    public void insertMember(Member m) throws InsertFailException {
        SqlSession session = getSqlSession();
       
        new MemberDao().insertMember(session, m);
       
        session.commit();
        session.close();
    }
   
}



어노테이션을 통해 인터페이스에 해당하는 메소드를 생성하지 않으면 오류가 발생하고


리턴 값이나 throw 같은 형식 또한 완전 일치된다.


이를 통해 팀 프로젝트 설계 시 충돌나는 부분을 최소화 할 수 있다.








또한 이와 같은 구조를 사용하는 이점으로는 유지보수가 훨신 수월하게 된다.


사용할 때 다음과 같이 사용하게 되는데


MemberService ms = new MemberServiceImpl();



        try {
            Member member = ms.selectMember(m);
           

        } catch (LoginFailException e) {

        }




생성을 MemberService ms = new MemberServiceImpl(); 이와같이 Interface로 선언하고


일반 클래스로 생성하는 방식으로 사용하게 된다.


이러한 방식을 의존성을 낮춘다고 한다.(의존성 역전, IoC라고 한다.)


(나중에 DI, Dependency Injection, 의존성 주입이라는 개념이 Spring Framework에 나온다.)


의존성이란 클래스 선언과 생성과의 관계로 한쪽이 바뀔 경우 한쪽도 같이 바꿔야 하는데 이러한 경우를 의존성이 강하다고 한다.


프로젝트 구조에 관해서는 다음에 자세히 설명.










해당 커스텀 Exception 처리를 통해 에러페이지로 포워딩 하는 부분이다.




순서는 ( controller -> Service -> dao ) 에서 데이터 처리 과정에 오류가 생기면 throw하여 controller에서 에러 페이지로 포워딩 하게 된다.







이 중 먼저 LoginFailException 부분이다.


public class LoginFailException extends Exception {
    public LoginFailException(String msg) {
        super(msg);
    }
}


Exception을 상속받는 클래스로 생성자를 통해 오류 메세지를 Exception 생성자로 전달.









Controller 부분으로 페이지에서 받은 값을 Service로 전달하여 DB를 조회한다.

try catch문에서 커스텀 예외처리를 적용한다.


        MemberService ms = new MemberServiceImpl();
        try {
            Member member = ms.selectMember(m);
           
            HttpSession session = request.getSession();
           
            session.setAttribute("loginUser", member);
           
            response.sendRedirect("index.jsp");
        } catch (LoginFailException e) {
            RequestDispatcher error = request.getRequestDispatcher("WEB-INF/views/common/errorPage.jsp");
            request.setAttribute("message", e.getMessage());
           
            error.forward(request, response);
        }









Service 부분의 인터페이스 부분.

    // 로그인용 메소드
    // public abstract가 전부 들어가기 때문에 애초에 생략해도 된다.(Interface)
    Member selectMember(Member m) throws LoginFailException;


왜 인터페이스를 만들어 메소드를 관리하는지는 다음 페이지에서 설명(https://qdgbjsdnb.tistory.com/226)










이 인터페이스를 받는 Service java 파일

public class MemberServiceImpl implements MemberService{
   
    @Override
    public Member selectMember(Member m) throws LoginFailException {
        // SqlSession은 MyBatis에서 커넥션 대신 사용
        SqlSession session = getSqlSession();
       
        Member member = new MemberDao().selectMember(session, m);
       
        session.close();
       
        return member;
    }
}

throws LoginFailException을 통해서 처리해준다.







Dao에서 DB를 조회하는 부분.

XML 파일을 통해 Mybatis 기능을 이용하는 부분

    public Member selectMember(SqlSession session, Member m) throws LoginFailException {
        Member member = null;
       
        member = session.selectOne("Member.loginMember", m);
       
        if (member == null) {
            session.close();
            throw new LoginFailException("로그인 실패");
           
        }
       
        return member;
    }


member = session.selectOne("Member.loginMember", m); - DB를 조회하는 Mybatis 기능

throw new LoginFailException("로그인 실패"); 을 통해서 조회한 DB가 없을 경우 커스텀 예외처리 생성자를 호출한다.

throws LoginFailException을 통해서 Exception으로 전달










에러가 발생한다면 계속 Throw 해서 처음 메소드를 호출했던 Mybatis 까지 와서 try catch 문에서

catch문이 실행되게 한다.

catch (LoginFailException e) {
            RequestDispatcher error = request.getRequestDispatcher("WEB-INF/views/common/errorPage.jsp");
            request.setAttribute("message", e.getMessage());
           
            error.forward(request, response);
        }






먼저 MyBatis는 저번의 Servlet 통신을 이용하여 DataBase 조회 방법이 매우 간단해진다.


XML 파일을 이용하여 간편하게 데이터를 조회하고 심지어 페이징 처리도 깔끔하게 가능하다.


이번 MVC 패턴의 폴더구조가 조금 바뀌는데 한번 확인해보자.


Spring Framework 형태에 맞추며 나중에 이해하기 쉬울 것 이다.







주목할 부분은


1. Model 부분의 Exception 추가(링크)


2. Model 부분의 Service 인터페이스 추가(링크)


3. FrontController를 통한 페이지 맵핑 처리(링크)


4. SQL 쿼리를 관리하던 Property 파일이 사라지고 XML 파일로 관리(링크)


5. web 아래에 resources 폴더를 추가하여 css image js uploadfile 등을 관리


6. view 파일들이 전부 WEB-INF 아래로 이동


6가지이다.










1. Model 부분의 Exception 추가(링크)



데이터 조회 시 오류 발생 시 커스텀 Exception을 이용하여


try catch 문에서 오류 페이지로 이동시켜준다.






2. Model 부분의 Service 인터페이스 추가(링크)



Service 부분에서 Interface를 이용하여 Service에 정의 할 메소드를 제한시키는 것.


Framework의 특징으로 미리 틀을 만들어 두는 느낌으로 이해하면 된다.


원래는 Dao에도 적용해야 한다.





3. FrontController를 통한 페이지 맵핑 처리(링크)



모든 맵핑 링크가 FrontController를 거치고 작업을 한 후


해당 서블릿으로 이동된다, 프로젝트의 규모가 커저도 페이지 관리가 편해진다.






4. SQL 쿼리를 관리하던 Property 파일이 사라지고 XML 파일로 관리(링크)



Mybatis의 핵심 기능으로 DB를 사용하기 매우 간편해진다.






5. web 아래에 resources 폴더를 추가하여 css image js uploadfile 등을 관리



핵심 파일들과 떨어뜨려 한 눈에 보기 편하게 관리






6. view 파일들이 전부 WEB-INF 아래로 이동









항목별로 설명이 필요한것은 새로 포스팅 합니다.







먼저 MyBatis jar파일을 라이브러리에 추가한다.


메이븐이라는 사이트를 이용


mvnrepository.com


메이븐은 나중에 나오지만 라이브러리들의 버전을 관리해주고 프로젝트 관리를 해주는 시스템? 이라고 한다.


위 사이트로 들어가 상단 검색창에 mybatis를 검색하자.








리스트에 나온 MyBatis를 클릭하고







해당 화면이 나오는데 여태껏 나온 버전별로 쭉 나온다.


버전별로 이용률도 한눈에 확인 할 수 있다.(Usages)


버전의 경우 X1.X2.X3 과 같은 형태로 관리된다.


X1의 경우는 하위 버전과 호환이 안되고 매우 크게 변화가 있는 경우이고


X2의 경우는 하위 버전과 호환이 되지만 일부 오류가 있을 수 있다.


X3의 경우는 보통 버그 픽스나 간단한 수정 등 작은 변경시 기록된다.




신버전은 항상 최신이여서 좋은것은 아니다.


신버전일 수록 사람들이 많이 사용해보지 않았고 버그가 발견되지 않아서 안정성 문제가 발생 할 수 있다는 점을 알아두자.



이 블로그에서 사용할 MyBatis 버전은 3.4.1이다. 3.4.1버전의 숫자를 눌러 다음 페이지로 넘어가자








jar 파일을 다음과 같이 lib 폴더에 추가하면 된다.















곧 Spring Framework에 대한 내용을 게시할 예정인데


Framework가 무엇인지 알아보자.


출처 - 네이버 어학사전




네이버 선생님의 어학사전에 따르면 뼈대, 틀, 체계와 같은 의미로 쓰인다.


어떤 단어가 기술 이름과 같은 곳에 쓰일때에는 절대 연관성이 없는 단어를 끌어다 쓰지 않는다.


그러므로 프로그래밍에서 Framework 또한 기존의 의미에서 벗어나지 않는다는 얘기다.


프로그래밍에서 쓰이는 Framework의 뜻은


개발자가 따르는 가이드나


개발자가 할 수 있는 개발의 범위를 지정하여 제한하거나


개발자가 사용할 수 있는 다양한 도구 플러그인을 말한다.





장단점이 중요한데


장점으로는


1. 개발 시간의 단축


2. 정형화 되어 일정 수준의 품질을 기대 할 수 있다.


3. 유지 보수가 쉽다.




단점으로는


1. 너무 의존하게 되면 개발자들의 능력이 떨어진다 (이 부분은 사전적인 장단점으로 사람마다 다르다고 생각한다.)


2. 어렵기 때문에 습득하는 시간이 오래걸린다





Framework의 종류로는 4가지가 있다.



영속성 Framework 

 데이터 저장, 조회, 변경, 삭제를 다루는 클래스 및 설정 파일들을 라이브러리화 하여 구현

Mybatis

Hibernate

자바 Framework

 Java EE를 통한 웹 어플리케이션 개발에 초점, 필요한 요소들을 모듈화

 Spring Framework

전자정부표준 - Spring

Struts

화면 구현 Framework

 Front-End를 보다 쉽게 구현할 수 있게 틀을 제공

 Bootstrap

Foundation

MDL

기능 및 지원 Framework

 특정 기능, 업무 수행에 도움을 줄 수 있는 기능을 제공

 Log4j

JUnit 5

ANT















이번 예는 결과로 보는 편이 편하다.

Formatting Tags의 예

먼저 Body 부분


    <h1 align="center">JSTL Fmt Library Tag Test</h1>
   
    <h2>fmt:formatNumber 태그 : 숫자에 포맷 적용하는 태그</h2>
   
    <c:set var="number" value="123456789"></c:set>
   
    <fmt:formatNumber value="${ number }" groupingUsed="true"></fmt:formatNumber>
    <br>

    <fmt:formatNumber value="1.234567" pattern="#.###"></fmt:formatNumber>
    <br>

    <fmt:formatNumber value="1.2" pattern="#.##"></fmt:formatNumber>
    <br>

    <fmt:formatNumber value="1.2" pattern="#.00"></fmt:formatNumber>
    <br>

    <fmt:formatNumber value="0.12" type="percent"></fmt:formatNumber>
    <br>

    <fmt:formatNumber value="123456789" type="currency"></fmt:formatNumber>
    <br>

    <fmt:formatNumber value="123456789" type="currency" currencySymbol="$"></fmt:formatNumber>
   
   
   
    <hr>
   
   
   
    <h2>fmt:formatDate 태그 : 날짜와 시간에 포맷 적용하는 태그</h2>

    <c:set var="today" value="<%= new java.util.Date() %>"></c:set>

    오늘 날짜 : <fmt:formatDate value="${ today }" type="date"/>
    <br>

    현재 시간 : <fmt:formatDate value="${ today }" type="time"/>
    <br>

    현재 날짜와 시간 : <fmt:formatDate value="${ today }" type="both"/>
    <br>

   
    <h2>날짜와 시간에 제공되는 포맷을 적용한 경우</h2>
   
    [default] : <fmt:formatDate value="${ today }" type="both" dateStyle="default" timeStyle="default"/>
    <br>

    [short] : <fmt:formatDate value="${ today }" type="both" dateStyle="short" timeStyle="short"/>
    <br>

    [medium] : <fmt:formatDate value="${ today }" type="both" dateStyle="medium" timeStyle="medium"/>
    <br>

    [long] : <fmt:formatDate value="${ today }" type="both" dateStyle="long" timeStyle="long"/>
    <br>

    [full] : <fmt:formatDate value="${ today }" type="both" dateStyle="full" timeStyle="full"/>
    <br>

   
    <h3>원하는 포맷으로 pattern 적용한 경우</h3>

    현재 날짜 : <fmt:formatDate value="${ today }" type="date" pattern="yyyy/mm/dd (E)"/>
    <br>

    현재 시간 : <fmt:formatDate value="${ today }" type="time" pattern="(a) hh:mm:ss"/>
    <br>

    현재 날짜와 시간 : <fmt:formatDate value="${ today }" type="both" pattern="yyyy/mm/dd (E) (a) hh:mm:ss"/>
    <br>



결과 화면은 다음과 같다.
















Function Tags의 예




    <h1 align="center">JSTL Function Library Tag Test</h1>


    <h3>문자열 처리에 관련된 함수들이다.</h3>
    <h3>el 안에서 값 처리용으로 사용된다.</h3>
   
    <c:set var="str" value="How are you?"></c:set>
   
    str : ${ str }
    <br>


    you가 포함되어 있나 : ${ fn:contains(str, 'you') }
    <br>


    how가 포함되어 있나 : ${ fn:contains(str, 'how') }
    <br>


    대소문자 구분 없이 how 포함되는지 확인 : ${ fn:containsIgnoreCase(str, 'how') }
    <br>


    모두 대문자로 : ${ fn:toUpperCase(str) }
    <br>


    모두 소문자로 : ${ fn:toLowerCase(str) }
    <br>


    are의 위치는 : ${ fn:indexOf(str, "are") }
    <br>


    How를 Where로 바꿈 : ${ fn:replace(str, "How", "Where") }
    <br>


    are 분리 추출 : ${ fn:substring(str, 4, 7) }
    <br>
   
    <c:set var="arr" value="${ fn:split(str, ' ') }"></c:set>


    <c:forEach items="${ arr }" varStatus="st">
        <c:out value="${ st.count } : ${ arr[st.index] }"></c:out>
    </c:forEach>
   
    <br>
   
    하나로 합치기 : <c:out value="${ fn:join(arr, '-') }"></c:out>
















https://qdgbjsdnb.tistory.com/217



먼저 위 게시글에서 라이브러리, jar 파일 다운로드가 필요하다.







JSTL은 JSP Standard Tag Library는 JSP에서 사용하는 커스텀 태그로 자주 사용하는 코드들의 집합을


사용하기 쉽게 태그형식으로 만들어 놓은 라이브러리다.


여태껏 써오던 <%%> 부분이 정말 간단하게 표현된다.


먼저 JSP 파일에 사용할 태그 라이브러리를 선언해야 한다.


라이브러리 종류는 다음과 같다.



Core Tags

 변수선언, url, 조건문, 반복문 등 포함

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

 Formatting Tags

 메시지 형식, 숫자, 날짜 등 포맷

<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

 Function Tags

 trim, substring과 같은 유용한 문자열 처리함수

<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

 XML Tags

 XML을 읽고 파싱처리 등에 필요한 라이브러리

<%@taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>

 SQL Tags

 JSP에서 DB를 연동, 쿼리 실행

<%@taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql"%>



해당 taglib prefix 선언을 페이지 상단에 명시하고 사용하면 된다.


이 중 아래의 XML, SQL은 사용을 지양한다.


XML의 경우는 JSON을 사용하여 좀 더 효율적으로 사용하기 때문이고


SQL의 경우는 프로젝트 구조(MVC 패턴)상 JSP에 사용하지 않는다. 유지보수에도 영향을 끼치므로 MVC 패턴을 따르는걸 추천






예제를 통해 라이브러리의 태그들을 어떻게 쓰는지 보자.



Core Tags의 사용법




 - c:set 태그 : 변수선언


    <!-- Scope 생략 시 기본 pageScope가 된다. -->
    <c:set var="num1" value="100" scope="session"></c:set>
    <c:set var="num2" value="200" scope="session"></c:set>


 - c:set 태그 : 배열 또는 컬렉션으로 사용할 문자열 선언


    <c:set var="colors">red, yellow, green, orange, blue, magenta</c:set>




선언된 변수들은 ${ num1 }과 같이 EL 태그로 사용하면 된다.



 - c:remove 태그 : 변수 삭제
   
    <!-- 삭제 시 scope를 지정하지 않으면 모든 scope의 동일 이름의 변수를 제거한다. -->
    <c:remove var="num1" scope="session"/>
    <c:remove var="num2" scope="session"/>





※ 이스케이프 시퀀스 : <, >, &, 등의 특수문자를 &lt;와 &gt;과 &amp;로 바꿔서 인식한다.




 - c:out 태그 : 값 출력용


    <c:out value="core라이브러리의 <out> 태그는 값을 화면에 출력하는 태그이다."></c:out>
    <!-- escapeXml false로 지정하면, <, > 등을 태그로 인식하고, true로 지정하면 문자로 인식한다. 기본값은 true이다. -->



    <c:out value="<h2>데이터 출력</h2>" escapeXml="false"></c:out><br>
    <c:out value="<h2>데이터 출력</h2>" escapeXml="true"></c:out><br>
   
    <h2>c:out 태그의 default 속성</h2>
    <!-- el로 가져오는 값이 없는 경우 대체할 값에 대해 default 값으로 설정 가능 -->
    <c:out value="${ param.name }" default="아무개님"/>




 - c:if 태그 : 조건문, if의 경우는 else가 따로 없으므로 다시 if태그를 사용하면 된다.


    <c:set var="value1" value="9" scope="page"/>
    <c:set var="value2" value="3" scope="page"/>
   
    <c:if test="${ value1 >= value2 }">
        <h3>value1이 큽니다.</h3>
    </c:if>


    <c:if test="${ value1 < value2 }">
        <h3>value2가 큽니다.</h3>
    </c:if>




 - c:choose : switch문, c:when : case문, c:otherwise : default문



<c:choose>
        <c:when test="${ no == 1 }">
            <h3>안녕하세요</h3>
        </c:when>


        <c:when test="${ no eq 2 }">
            <h3>반갑습니다.</h3>
        </c:when>


        <c:otherwise>
            <h3>환영합니다.</h3>
        </c:otherwise>
</c:choose>




 - c:forEach태그 : for문


    <c:forEach begin="1" end="10">
        반복실행<br>
    </c:forEach>


   
    <c:forEach var="size" begin="1" end="7" step="1">
        <font size="${ size }">글자크기 ${ size }</font>
    </c:forEach>




 - c:forEach 태그 : 배열 또는 컬렉션 연속 처리에 for~each문처럼 사용할 수 있기에 매우 편리하다.


    <c:forEach var="color" items="${ colors }" varStatus="st">
        <font color="${ color }">
            ${ st.count } : 글자색 ${ color }<br>
        </font>
    </c:forEach>






 - c:forTokens 태그 : 문자열을 토큰으로 분리 처리할 때 사용 (split과 같다)
    <ul>
        <c:forTokens var="color" items="yellow blue pink red green" delims=" ">
            <li>${ color }</li>
        </c:forTokens>
    </ul>



  - 여러개의 토큰 문자를 가지고 분리 처리할 수도 있음


    <c:forTokens var="color" items="yellow-blue*pink/red green" delims="/*- ">
        ${ color }<br>
    </c:forTokens>






- c:url 태그 : 링크 설정 정보 별도 지정시 사용하는 태그 , param을 이용하여 값을 넘길 수 있다.


    <c:url var="fmtlink" value="testJstlCoreResult.jsp">
        <c:param name="num" value="77"></c:param>
    </c:url>


    <a href="${ fmtlink }">결과화면 연결</a>











EL, Expression Language는 JSP 2.0에 추가되어


<%=%> 와 같은 jsp에 쓰는 java 코드를 간결하게 사용하는 방법





EL의 연산자 기호는 다음과 같이 있다.


사용하는데 일반 연산자를 써도 상관없긴 하다.




 일반 연산자

 EL 기호 연산자

+, -

 +, -

 *, /

*, div

 %

 mod

 &&

and

 ||

or

 !

not

 >

lt(less than)

 <

gt(greater than)

 >=

 le(less or eqaul)

 <=

 ge(greater or equal)

 ==

eq(equal)

 !=

 ne(not equal)

 value == null

 empty






EL 태그의 내장 객체


pageScope

 page영역의 객체에 접근

 requestScope

 request영역의 객체에 접근

 sessionScope

 session영역의 객체에 접근

 applicationScope

 application영역의 객체에 접근

 param

 전달된 파라미터값을 받아올 때

 paramValues

 전달된 파라미터값을 배열로

 header

 특정 헤더 정보를 받아올 때

 headerValues

 특정 헤더 정보를 배열로 받아올 때

 cookie

 ${cookie.key} 형식으로 쿠키값 조회

 initParam

 초기 파라미터를 조회

 pageContext

 pageContext 경로 조회










EL 태그의  간단한 예



전달된 request 객체에 저장된 정보 출력 예



name : ${ requestScope.name }
age : ${ requestScope.age }
phone : ${requestScope.phone }


name : ${ name }
age : ${ age }
phone : ${ phone }







ArrayList items = (ArrayList) request.getAttribute("items");


ArrayList와 같이 전달된 값을 출력하는 예



0 : ${ requestScope.items[0] }
1 : ${ requestScope.items[1] }
2 : ${ requestScope.items[2] }
   
0 : ${ items[0] }
1 : ${ items[1] }
2 : ${ items[2] }






전달된 객체의 필드 값들 출력 예, member라는 객체를 request 형식으로 받음



이름 : ${ member.name }
나이 : ${ member.age }
전화번호 : ${ member.phone }
이메일 : ${ member.email }






Parameter로 넘어온 값 출력



    <%
        String pName = request.getParameter("name");
        int price = Integer.parseInt(request.getParameter("price"));
        String[] pNo = request.getParameterValues("no");
        String option = request.getParameter("option");
    %>


이와 같이 받던 데이터를 아래와 같이 간단하게


    상품명 : ${ param.name }
    가격 : ${ param.price }
    제품번호 : ${ paramValues.no[0] }와 ${ paramValues.no["1"] }
    옵션 : ${ (empty param.option)?"옵션없음":param.option }






스코프를 이용하여 구분지어 출력



Request

    이름 : ${ requestScope.member.name }
    나이 : ${ requestScope.member.age }
    전화번호 : ${ requestScope.member.phone }
    이메일 : ${ requestScope.member.email }
   

      Session
    이름 : ${ sessionScope.member.name }
    나이 : ${ sessionScope.member.age }
    전화번호 : ${ sessionScope.member.phone }
    이메일 : ${ sessionScope.member.email }
   
    동일한 이름의 네이밍이 있다면 request가 우선되어 출력됨

    이름 : ${ member.name }
    나이 : ${ member.age }
    전화번호 : ${ member.phone }
    이메일 : ${ member.email }





간단한 예 이외에 내장 객체들은 다음 게시글에서 쭉 사용한다.




+ Recent posts