Servlet을 사용할 땐 SHA-512 방식의 암호화를 사용해 보았는데 이번엔


BCrypt 방식을 이용해보자.


예제는 회원가입 시 Controller에서 입력받은 암호를 암호화 처리를 통해 변경하는 작업



MemberController.java


@Controller
public class MemberController {
    // 의존성 주입용 필드 선언
    // 어노테이션 + Interface 타입
    @Autowired
    private MemberService ms;
    // 인터페이스를 상속받는 객체에 @Component나 @Service 어노테이션이 필요
    // 객체 생성 시 new Service와 같은 부분을 Spring에게 맡기는것.
   
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;


    @RequestMapping("insert.me")
    public String insertMember(Member m, Model model) {
       
        System.out.println("member : " + m);
       
        String encPassword = passwordEncoder.encode(m.getUserPwd());
       
        System.out.println(encPassword);
       
        m.setUserPwd(encPassword);
       
        int result = ms.insertMember(m);
       
        if (result > 0) {
            return "redirect:goMain.me";
        } else {
            model.addAttribute("msg", "회원가입 실패!");
            return "common/errorPage";
        }
       
    }

}



먼저 bcrypt란?
DB에 비밀번호를 저장할 목적으로 설계된 알고리즘

단방향 해쉬 함수는 암호화된 메세지를 수학적인 연산을 통해 암호화 된 메세지인 다이제스트를 생성한다.


원본 메세지를 가지고 암호화된 메세지를 복호화 할 수 있는 것을 양방향이라고 하고


암호화된 메세지를 복호화 할 수 없는 것을 단방향이라고 한다.


다이제스트란 결과값들이라고 생각하면 된다.


단방향 해쉬 함수도 사용하면 안되는 이유


1. 단방향 해쉬함수는 많은 다이제스트가 확보되면 평문을 찾아낼 수 있다.


2. 비밀번호를 저장하기 위한 목적으로 설계된 알고리즘이 아닌,


검색을 위해 설계된 알고리즘이다.


그만큼 다이제스트의 생성이 매우 빨라 뚫리는데 시간이 짧다.


이를 해결하기 위해 슬링(salting)기법이 추가되었다.


원본 메세지에 문자열을 추가하여 동일한 길이의 다이제스트를 생성하는 것을 슬링이라 한다.

하지만 salt 값을 알아내면 나머지는 단방향 해쉬함수를 통한 다이제스트를 복호화 하는 것과 별 차이가 없다.


bcrypt 방식은 이러한 salt값을 랜덤하게 생성하여 암호화를 하는 방식이다.


추가적으로 다이제스트를 생성하는데 걸릴 시간을 결정할 수도 있다.


생성 시간에 오래 걸린다면 대량의 다이제스트 생성 역시 오래걸리게 된다.


1999년에 발표되어 현재까지 자주 사용되는 강력한 비밀번호 저장용 매커니즘이다.





설명은 이렇고 사용 자체는 라이브러리 등록, 선언


암호화 메소드 or 암호 일치하는지?에 대한 메소드 선언밖에 없는 간단한 방법이다.






작업 순서로는


메이븐 시큐리티 라이브러리 추가 - Autowired 추가 - security xml 추가 - web.xml에 추가된 xml들 추가이다.







메이븐 시큐리티 라이브러리 pom.xml에 추가한다. - https://qdgbjsdnb.tistory.com/237


Autowired 추가는 빨간글자로 표시해 두었다.



다음은 web.xml 파일 추가부분


/WEB-INF/config/spring-security.xml 추가


    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:root-context.xml
            /WEB-INF/config/spring-security.xml
        </param-value>
    </context-param>



/WEB-INF/config/spring-security.xml의 설정을 읽어오라고 명시하였으니


해당 경로에 파일을 생성한다.


<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
   



    <beans:bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></beans:bean>
   



</beans:beans>






기존에 있는 기본 beans 선언 안에


<beans:bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></beans:bean>


BCryptPasswordEncoder Bean 선언만 하면 끝난다.








메소드의 경우는 BCryptPasswordEncoder 로 선언된 객체를 통해


encode(String형)을 사용하면 String형이 암호화되고


matches(입력한 암호, DB에 저장된 암호)를 입력했을 땐 암호가 일치하는치 체크해준다.(true or false)





        String encPassword = passwordEncoder.encode(m.getUserPwd());



        if (!passwordEncoder.matches(m.getUserPwd(), encPassword)) {
            throw new LoginException("로그인 실패!");
        } else {
            loginUser = md.selectMember(sqlSession, m);
        }






먼저 Servlet 프로젝트에서 사용했던 가장 익숙한 방법.


예제는 전부 login.me라는 맵핑 주소를 통해 연결되고 POST 방식으로 전달 되었다.




1. HttpServletRequest, HttpServletResponse를 이용



    // 1. 서블릿에서 했던 방식대로 HttpServletRequest, HttpServletResponse를
    // 매개변수로 선언하면 스프링 컨테이너가 이 메소드를 실행할 때 자동으로 두 객체를 인자로 주입해준다.
    @RequestMapping("/login.me")
    public String loginCheck(HttpServletRequest request, HttpServletResponse response) {
        String userId = request.getParameter("userId");
        String userPwd = request.getParameter("userPwd");
       
        System.out.println(userId + "  " + userPwd);
       
        return "main/main";
    }



2. 어노테이션 RequestParam을 이용하여 값을 받아오기



    // 2. RequestParam 어노테이션을 이용해서 파라미터 값 받아오기
    // => 스프링에서는 조금 더 간단하게 파라미터를 받아올 수 있는 방법을 제공한다(RequestParam)
    @RequestMapping(value="/login.me", method=RequestMethod.POST)
    public String loginCheck(@RequestParam("userId")String userId, @RequestParam String userPwd) {
        System.out.println(userId + "  " + userPwd);
       
        return "main/main";
    }


@RequestParam("전달받은 name") 처럼 name을 명시해도 되고


아니면 ("")을 생략해도 자동으로 잡히기 때문에 편한 방식으로 사용하면 된다.






3. 어노테이션 RequestParam은 생략이 가능하다.




// 3. RequestParam 어노테이션은 생략해도 파라미터값을 가져와서 매개변수에 저장할 수 있다.
    @RequestMapping(value="login.me", method=RequestMethod.POST)
    public String loginCheck(String userId, String userPwd) {
        System.out.println(userId + "  " + userPwd);
       
        return "main/main";
    }


생략을 통해서 구현하면 본인은 괜찮지만 나중에 봤을 때나 다른사람이 봤을 때


못알아 볼 수 있기 때문에 그냥 명시하는것을 추천





4. @ModelAttribute를 이용, 객체에 자동으로 Setter을 이용하여 값을 가져온다.




    // 4. @ModelAttribute를 이용한 값 전달받는 방법(커맨드 객체)
    @RequestMapping(value="login.me", method=RequestMethod.POST)
    public String loginCheck(@ModelAttribute Member m) {
        System.out.println(m);
       
        return "main/main";
    }

해당 객체에 Setter 메소드가 있어야 되고


전달받은 name과 해당 객체의 필드명이 같아야함.


오타에 주의하자.




5. @ModelAttribute를 생략하는 방법 + 값 리턴방법(예전에 쓰던 request, response)




    // 5. 위의 어노테이션을 생략하고 객체로 받는 방법
    @RequestMapping(value="login.me", method=RequestMethod.POST)
    public String loginCheck(Member m, HttpServletRequest request, HttpServletResponse response) {
        System.out.println(m);
       
        MemberService ms = new MemberServiceImpl(); // 결합을 느슨하게 만듦
       
        try {
            Member loginUser = ms.loginMember(m);
           
            request.getSession().setAttribute("loginUser", loginUser);
           
            return "redirect:goMain.me";
        } catch (LoginException e) {
            request.setAttribute("msg", e.getMessage());
            return "common/errorPage";
        }
       
        return "main/main";
    }


Member m만 입력해도 Spring이 자동으로 맞춰준다.






6. ModelAndView라는 객체를 사용하여 값을 리턴



    // 6. ModelAndView로 리턴 처리
    // => model은 뷰로 전달할 데이터를 앱 형식으로 담을 때 사용하는 객체로 scope는 request이다.
    // => view는 requestDispatcher처럼 forward할 뷰 페이지 정보를 담은 객체이다.
    // => ModelAndView는 이 두가지를 합쳐 놓은 객체이며
    // => Model 객체를 따로 사용하는 것도 가능하다.
    // => 하지만 view 객체는 따로 사용하지 못한다.
    @RequestMapping("login.me")
    public ModelAndView loginCheck(Member m, ModelAndView mv, HttpSession session) {
        try {
            Member loginUser = ms.loginMember(m);
           
            session.setAttribute("loginUser", loginUser);
           
            mv.setViewName("redirect:goMain.me");
           
        } catch (LoginException e) {
            mv.addObject("msg", e.getMessage());
            mv.setViewName("common/errorPage");
        }
       
       
        return mv;
    }


ModelAndView는 리턴 받는 페이지의 정보도 담아가고 리턴 받을 값도 전부 담아서


메소드 리턴타입으로 돌려주는걸 확인 할 수 있다.





7. model 객체로 값을 리턴하고 String 리턴으로 뷰 이동



    // 7. Model 객체를 따로 사용하고 String으로 뷰 이름을 리턴하는 방법
    @RequestMapping("login.me")
    public String loginCheck(Member m, Model model, HttpSession session) {
        Member loginUser;
        try {
            loginUser = ms.loginMember(m);
           
            session.setAttribute("loginUser", loginUser);
           
            return "redirect:goMain.me";
        } catch (LoginException e) {
            model.addAttribute("msg", e.getMessage());
           
            return "common/errorPage";
        }
       
        return null;
    }



8. @SessionAttributes, model 객체를 사용하여 Session 리턴


@SessionAttributes("loginUser")
@Controller
public class MemberController {


    // 8. session에 저장을 할  때 @SessionAttributes 사용하기
    // => 모델에 Attribute가 추가될 때 자동으로 키 값을 찾아 세션에 등록하는 기능을 제공하는
    //    어노테이션이다.
    @RequestMapping("login.me")
    public String loginCheck(Member m, Model model) {
       
        try {
            Member loginUser = ms.loginMember(m);
           
            model.addAttribute("loginUser", loginUser);
           
            return "redirect:goMain.me";
           
        } catch (LoginException e) {
            model.addAttribute("msg", e.getMessage());
            return "common/errorPage";
        }
       
    }

}

@SessionAttributes("loginUser") 라고 명시를 해 두면


model.addAttribute("loginUser", loginUser);라고 추가했을 때 자동으로


Session으로 추가된다.











보너스로 세션 파기하는 방법


    @RequestMapping("logout.me")
    public String LogOutputStream(HttpServletRequest request) {
        request.getSession().invalidate();
       
        return "redirect:goMain.me";
    }


    @RequestMapping("goMain.me")
    public String goMain() {
        // 포워드만 해주는 메소드
        return "main/main";
    }


리턴을 통해서 바로 페이지로 돌아가게 되면


새로고침 시 get, post 값을 한 번 더 전송할 수 있다.


이를 방지하기 위해 포워드만 해주는 메소드로 redirect 우회하면 된다.









※ 위의 메소드들에서 사용하는 ms.***** ms 객체는


@Autowired로 생성한 bean 객체


db에서 로그인 정보를 확인해오는 객체이다.




Spring Framework 부터는 라이브러리를 xml파일에 명시하고


이를 토대로 Maven이라는 툴이 자동으로 다운로드 해서 관리 해줄 것이다.


먼저 해당 파일의 xml 내용을 확인해보자


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.kh</groupId>
    <artifactId>tsp</artifactId>
    <name>testSpringProject</name>
    <packaging>war</packaging>
    <version>1.0.0-BUILD-SNAPSHOT</version>
    <properties>
        <java-version>1.8</java-version>
        <org.springframework-version>5.0.8.RELEASE</org.springframework-version>
        <org.aspectj-version>1.8.13</org.aspectj-version>
        <org.slf4j-version>1.7.25</org.slf4j-version>
    </properties>
    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework-version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- AspectJ -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${org.aspectj-version}</version>
        </dependency>

        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j-version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.mail</groupId>
                    <artifactId>mail</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
            <scope>runtime</scope>
        </dependency>

        <!-- @Inject -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>

        <!-- Servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- Test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>

        <!---------------------------------------- 필요한 추가 라이브러리는 여기 밑에다 등록하면 됨---------------------------------------->

       
        <!---------------------------------------- 필요한 추가 라이브러리는 여기 밑에다 등록하면 됨---------------------------------------->

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>








이 파일은 Legacy Project를 생성하면 자동생성 된다.


주목해서 볼 부분은


    <properties>
        <java-version>1.8</java-version>
        <org.springframework-version>5.0.8.RELEASE</org.springframework-version>
        <org.aspectj-version>1.8.13</org.aspectj-version>
        <org.slf4j-version>1.7.25</org.slf4j-version>
    </properties>



Properties의 기본 버전 설정들과


    <dependencies>
        <!-- Spring -->
        <dependency>


dependencies안에서 시작하는 dependency 태그들이다.


dependency 태그들이 우리가 원하는 라이브러리를 명시하는 부분이고


Maven 툴은 이 파일을 읽어 명시한 dependency 태그를 토대로 다운로드 및 업데이트를 진행한다.






이 dependency 태그들은 MVNrepository 라는 페이지에서 복사해서 붙여넣기만 하면 된다.


https://mvnrepository.com/


위 사이트로 들어가 맨 위 검색창에 원하는 라이브러리를 검색하고





ojdbc6을 예로 든다.


원하는 라이브러리를 클릭





원하는 버전을 클릭, 원래는 수십개씩 나열되어 있다.


선택 기준은 Usages, 사용율이 높은 쪽을 선택하는걸 추천한다.


버전이 높다고 무조건 좋은건 아니다.


다른 라이브러리와 충돌이 나는 경우도 많기 때문에...


버전을 클릭하면 다음 화면이 나온다.




이 화면에서 Maven 탭을 누르면 다음과 같이 dependency 태그를 준다.


이 태그를 복사해서 pom.xml에 복사하고 저장하면 Maven 업데이트가 시작된다.





본 블로그에서 사용할 라이브러리들은 다음과 같이 추가하였다.


테스트 당시 충돌은 없었고 몇번의 다운로드 오류로


repository 폴더의 라이브러리들을 몇번 다 지웠다가 다시 다운로드 했다.


충돌이 나거나 다운로드를 잘못 했다는 기준은


Problems 탭에 빨간색 글씨로 오류가 발견되거나


해당 프로젝트를 톰캣에 올리고 시작하면


서버가 다 켜지기도 전에 오류 발생 메세지를 띄운다.


오류 발생 시 그냥 톰캣 오류라고 뜨기 때문에 오류를 찾기 힘들다.


그러므로 편하게 라이브러리 전부 지우고 다시 설치하고 반복하는게 제일 빠르다.




라이브러리가 많아지면 프로젝트가 무거워지고 톰캣 구동시간이 길어질 수 있다.








해당 블로그에서 사용 할 dependency 태그들은 다음과 같다.


        <!-- jdbc 라이브러리 추가 -->
        <dependency>
            <groupId>com.jslsolucoes</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.1.0</version>
        </dependency>

        <!-- 마이바티스 라이브러리 추가 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- spring-jdbc 추가 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- 마이바티스 스프링 모듈 추가 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- dbcp 라이브러리 추가 -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>

        <!-- 스프링 시큐리티 모듈 추가 -->
        <!-- core, web, config 가 필요하다. -->
        <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>

        <!-- AOP관련 라이브러리 추가 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
       
        <!-- jackson 추가 -->
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.5</version>
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.5</version>
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/org.codehaus.jackson/jackson-mapper-asl -->
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
       
        <!-- Json lib ext 추가 -->
        <!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib-ext-spring -->
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib-ext-spring</artifactId>
            <version>1.0.2</version>
        </dependency>






다음의 포스트부터는 이 라이브러리들이 정상적으로 받아져서


톰캣 실행이 잘 된다는 가정하에 포스트함




사용한 버전들은 version 태그에 잘 써있음












+ Recent posts