728x90
300x250

[Spring-Framework] 18. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (3)


이전 글을 보지 않았다면, 꼭 진행해보고 오길 권장한다.


[이전 글]

1. [Spring-Framework] 16. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (1)
https://yyman.tistory.com/1422


2. [Spring-Framework] 17. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (2)

https://yyman.tistory.com/1423




22. View - home.jsp


사용자 인터페이스는 아래의 그림처럼 표현하였다.



그림 26. 로그인 전 - "/" 페이지



그림 27. 로그인 후 - "/" 페이지



그림 28. 로그인 후 - 권한별 기능 표시, 계정 정보 출력



그림 29. 로그인 후 - 권한별 기능 표시, 계정 정보 출력



경로: /src/main/webapp/WEB-INF/views/home.jsp



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

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

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page session="false" %>

<html>

<head>

<title>Spring-Security 5 (Java 방식)</title>

<meta charset="UTF-8">

<style>

body{

font-family:'Arial';

font-size:12px;

}


a{

text-decoration:none;

color:#666;

}

</style>

</head>

<body>

<h1>

Hello world!(Spring-Security 5(Java 방식)) - DB연동(Oracle)

</h1>

<hr />

<sec:authorize access="isAnonymous()">

<!-- 로그인 전 -->

<p>

<a href="<c:url value="/member/loginForm" />">로그인</a>

</p>

</sec:authorize> 

<sec:authorize access="isAuthenticated()">

<!-- 로그인 성공 -->

<form:form action="${pageContext.request.contextPath}/logout" method="POST">

<input type="submit" value="로그아웃" />

</form:form>

<p>

<!-- 방법1. Sec 적용(이름 출력) -->

방법(sec 태그)1: <sec:authentication property="name" />

</p>

<p>

<!-- 방법2. c태그, Controller에서 가져오기 -->

방법(Model 정의)2: ${username}

</p>

</sec:authorize> 


<h3>

<!-- 관리자 권한을 가진 경우만 보이기 -->

<sec:authorize access="hasRole('ROLE_ADMIN')" >

<a href="<c:url value="/admin/home" />">관리자 홈</a>&nbsp;&nbsp;

</sec:authorize>

<a href="<c:url value="/encode-password?password=pass" />">비밀번호</a>

</h3>


<!-- 비밀번호 생성기 -->

<c:set var="gene_pwd" value="${encode}" />

<c:if test="${gene_pwd != null}">

    <c:out value="${gene_pwd}" />

</c:if>


</body>

</html>


파일명: home.jsp


[첨부(Attachments)]

home.zip





23. View - admin/home.jsp


관리자 페이지에 관한 것이다.



그림 30. 관리자 페이지


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

<%@ page session="false" %>

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java" %>

<html>

<head>

<meta charset="UTF-8">

<title>Admin - Page(관리자 - 페이지)</title>

<meta charset="UTF-8">

</head>

<body>

<h1>

Hello world!

</h1>


<P>

<h3>[<a href="<c:url value="/" />">홈으로(Home)</a>]</h3>

</P>

</body>

</html>



파일명: home.jsp


[첨부(Attachments)]

home.zip




24. View - member/loginForm.jsp


로그인 폼에 대한 사용자 인터페이스이다.



그림 31. 로그인 폼 - 로그인 전



그림 32. 로그인 폼 - 로그인 시도(아이디 또는 비밀번호 틀렸을 때)


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

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

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<!DOCTYPE html>

<html lang="ko">

<head>

<meta charset="UTF-8">

    <title>로그인 - 페이지(Login - Page)</title>

    <style>

    body{

    font-family:'Arial';

    font-size:12px;

    }

    a{

    text-decoration:none;

    color:#666;

    }

    </style>

</head>

<body>


<h1>아이디와 비밀번호를 입력해주세요.</h1>

<hr />


<c:url value="/member/loginForm" var="loginUrl" />

<form:form name="f" action="${loginUrl}" method="POST">

    <p>

        <label for="username">아이디</label>

        <input type="text" id="id" name="id" />

    </p>

    <p>

        <label for="password">비밀번호</label>

        <input type="password" id="password" name="password"/>

    </p>

    <p>

        <label for="remember-me">Remember-me(로그인 상태 유지)</label>

        <input type="checkbox" id="remember-me" name="remember-me"/>

    </p>

    

    <%-- <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> --%>

    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

    <button type="submit" class="btn">로그인</button>

    

    <!-- 에러 메시지 영역 -->

    <c:if test="${param.error != null}">

        <p>아이디와 비밀번호가 잘못되었습니다.</p>

    </c:if>

    <c:if test="${param.logout != null}">

        <p>로그아웃 하였습니다.</p>

    </c:if>


</form:form>

<h3>[<a href="<c:url value="/" />">홈으로(Home)</a>]</h3>


</body>

</html>


파일명: loginForm.jsp


[첨부(Attachments)]

loginForm.zip




25. View - member/accessDenied.jsp


접근 제한 페이지에 관한 소스이다.



그림 33. 접근 제한된 페이지


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

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

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<!DOCTYPE html>

<html lang="ko">

<head>

    <meta charset="UTF-8">

    <title>Access Denied</title>

</head>

<body>

<h1>Access Denied!</h1>

<h3>[<a href="<c:url value="/" />">홈</a>]</h3>

</body>

</html>


파일명: accessDenied.jsp


[첨부(Attachments)]

accessDenied.zip



26. 맺음글(Conclusion)


Spring Framework 5.4, Spring Security 5.4, Oracle 19g를 활용하여 보안 영역에 대해서 자세하게 살펴보았다.



* 참고 자료(References)


1. Hello Spring Security Java Config, https://docs.spring.io/spring-security/site/docs/5.0.16.RELEASE/guides/html5/helloworld-javaconfig.html#setting-up-the-sample, Accessed by 2020-09-27, Last Modified 2020-05-06.


추천(30점): 공식 사이트에서 제공하는 메뉴얼인데, 제작 흐름을 파악할 수 있다.


2. Tomcat ->Multiple Contexts have a path of 에러, https://kkangdda.tistory.com/12, Accessed by 2020-09-27, Last Modified 2019-11-06.


3. Spring Security 5 – Java Config, https://howtodoinjava.com/spring5/security/security-java-config-enablewebsecurity-example/, Accessed by 2020-09-27, Last Modified 2020-06-19.


추천(50점): June 19, 2020

- 1. @EnableWebSecurity is not found in any of the 3 jars. 

= 2. "spring-security-config-5.0.7.RELEASE", "spring-security-core-5.0.7.RELEASE", "spring-security-web-5.0.7.RELEASE", 

     - June 19, 2020, Spring version is 5.2.5


4. Spring 4.0 + Java Config - web.xml 없애기..., https://tiveloper.tistory.com/entry/Spring-40-Java-Config-webxml-없애기, 2014-10-24

추천(20점): 조금 코드가 바뀐 부분이 있다. 

public class WebInitializer implements WebApplicationInitializer { }
= 이 부분 클래스가 동작하지 않음. (변동 사항이 있었음.)
   - 2013년 12월 정도에 Spring 4.0이 출시되었으니깐 오래되었다고 봐도 됨.


5.Spring Security : Web MVC + Security - Custom Login Form 만들기, https://kogle.tistory.com/78?category=870263, Accessed by 2020-09-27, Last Modified 2020-05-16.


추천(25점): 커스텀 로그인 페이지 java 버전에 대해서 간결하게 잘 작성되어 있음. (그러나 태스트를 해본 바로는 완벽하게 동작하진 않음.)


6. Spring Security Authentication Provider, https://www.baeldung.com/spring-security-authentication-provider, Accessed by 2020-09-27, Last Modified 2020-08-19.


추천(40점): 국내 자료에는 "CustomAuthenticationProvider()"에 대해서 자세히 잘 적어놓은 글을 찾기 힘들었음.

- shouldAuthenticateAgainstThirdPartySystem()가 동작되지는 않았지만, 많은 도움이 되었음.


6.Spring Security - 인증 절차 인터페이스 구현 (1) UserDetailsService, UserDetails, https://to-dy.tistory.com/86?category=720806, Accessed by 2020-09-27, Last Modified 2018.


추천(25점): 인증 절차(쉽게 소개하면, "DB처리에 대한 방법과 과정")에 대해서 잘 작성하였음.

- iBatis(MyBatis)로 작성되어 있긴 한데, 구현 원리를 살펴볼 수 있었음.


7.How to refer brcypt encoder to customized authentication provider?, https://stackoverflow.com/questions/35900053/how-to-refer-brcypt-encoder-to-customized-authentication-provider, Accessed by 2020-09-27, Last Modified 2016.


추천(13점): customized authentication provider에 대해서 구현 원리와 암호 처리 등에 대해서 소개하고 있다.

이 글에서 알 수 있었던 중요한 부분은 Spring Security 5.4로 구현하면서 암호가 랜덤으로 처리되는 것을 확인하였는데, 암호 확인하는 방법을 찾던 도중에 Bcrypt 함수 내에 확인하는 함수가 있는 것을 알게 되었다.


8. [Spring/Security] 초보자가 이해하는 Spring Security, https://postitforhooney.tistory.com/entry/SpringSecurity-%EC%B4%88%EB%B3%B4%EC%9E%90%EA%B0%80-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-Spring-Security-%ED%8D%BC%EC%98%B4, Accessed by 2020-09-27, Last Modified 2017-03-31.


9. [SPRING SECURITY] 4.스프링 시큐리티 로그인 커스터마이징, https://debugdaldal.tistory.com/89, Accessed by 2020-09-27, Last Modified 2016.

추천(15점): 자바 기반으로 커스터마이징 구현하는 방법에 대해서 소개되어 있다.


10. Spring Security Custom AuthenticationProvider example, https://javaengine.tistory.com/entry/Spring-Security-Custom-AuthenticationProvider-example, Accessed by 2020-09-27, Last Modified 2017-02-06.

추천(20점): Spring Security with Java 프로젝트 환경설정만 잘 되어 있다면, 시도해봐도 괜찮은 글이다.


11. Spring Security Remember Me, https://www.baeldung.com/spring-security-remember-me, Accessed 2020-09-27, Last Modified 2020-08-15.

추천(15점): 로그인 유지 및 자동 로그인에 관한 자바 버전으로 구현하는 방법에 대해서 소개하고 있다.
쿠키 제거하고 몇 가지 간단한 사용방법만 소개하고 있다.


12. 6 Spring Security - Access Denied Handler(error page 처리), https://jungeunlee95.github.io/java/2019/07/18/6-Spring-Security-Access-Denied-Handler(errorpage-%EC%B2%98%EB%A6%AC)/, Accessed by 2020-09-27, Last Modified 2019-7-18.

-> 비고: 접근 제어 페이지에 대해서 소개하고 있음.


13. 3 Spring Security - Authorization(권한) 설정(ROLE), TagLib authorize 추가, https://jungeunlee95.github.io/java/2019/07/18/3-SpringSecurity-Authorization(%EA%B6%8C%ED%95%9C)-%EC%84%A4%EC%A0%95(ROLE),-TagLib-authorize-%EC%B6%94%EA%B0%80/, Accessed by 2020-09-27, Last Modified 2020-07-18.

-> 비고: 권한별 페이지 꾸리는 방법에 대해서 소개하고 있음.

반응형
728x90
300x250

[Spring-Framework] 17. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (2)


이번에는 이전 글에 이어서 Spring-Security를 구현해보려고 한다.

조금 이번 글부터는 난이도가 있어지니깐 개발 전략을 잘 숙지해서 작업하면 좋겠다.


1. [Spring-Framework] 16. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (1), 2020-09-27

https://yyman.tistory.com/1422



14. 개발 전략


하나 만들면 다 되는 게 아니다. 계속 연속해서 복합적으로 수정작업을 해줘야 한다.

그래서 개발 작업이 힘이 든다. 쉽지만 않다.


그림 24. 개발 전략도


SecurityWebApplicationInitializer.java, SecurityConfig.java는 Spring Security 구현에 있어서 핵심이라고 해도 무방하다.

두 개를 잘 구현한다면, 셈플 로그인 페이지는 볼 수 있다.


문제는 자바 버전으로 구현했을 때 보안 토큰 절차가 xml방식에 비해서 매우 까다롭게 반응한다는 것이다.

그래서 간단한 코드로 태스트를 해보기도 전에 DB 설계를 할 수 밖에 없었다.


이유는 토큰 인증 때문에 그렇다.


SqlMapSessionFactory도 계속 반복해서 다양한 영역에서 재사용될 것이다.



15. config의 SecurityWebApplicationInitializer.java (필수 파일)


이 파일을 보면, 제일 황당한 생각이 들 수 밖에 없는 이유가 코드는 몇 줄 안 되는데, 없으면 동작이 안 된다는 것이다.


package com.springMVC.javaSecurity5.config;


import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;


public class SecurityWebApplicationInitializer 

                        extends AbstractSecurityWebApplicationInitializer {


}


파일명: SecurityWebApplicationInitializer.java


[첨부(Attachments)]

SecurityWebApplicationInitializer.zip



16. config의 SecurityConfig.java (필수 파일)


"SecurityConfig.java" 이 파일도 없으면 Spring Security with 자바 버전이 동작되지 않는다.


패키지 경로: com.springMVC.javaSecurity5.config


package com.springMVC.javaSecurity5.config;


import javax.sql.DataSource;


import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import org.springframework.security.web.csrf.CsrfFilter;

import org.springframework.web.filter.CharacterEncodingFilter;


import com.springMVC.javaSecurity5.db.SqlMapSessionFactory;

 

@Configuration

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

 

    @Autowired

    PasswordEncoder passwordEncoder;

    

    @Autowired

    private CustomAuthenticationProvider authProvider;

    

    protected void configure(AuthenticationManagerBuilder auth, HttpSecurity http) throws Exception {

      

    CharacterEncodingFilter filter = new CharacterEncodingFilter();


    /* UTF-8 한글 보완 */

        filter.setEncoding("UTF-8");

        filter.setForceEncoding(true);

        http.addFilterBefore(filter,CsrfFilter.class);

   

        auth

        .authenticationProvider(authProvider);

        

    /* 현재 - 임시

        auth.inMemoryAuthentication()

        .passwordEncoder(passwordEncoder)

        .withUser("user").password(passwordEncoder.encode("1234")).roles("RULE_USER")

        .and()

        .withUser("admin").password(passwordEncoder.encode("1234")).roles("RULE_USER", "RULE_ADMIN");

        

         */

        

        // withUser("admin").password(passwordEncoder.encode("1234")).roles("USER", "ADMIN");

      

        /* 임시

   

    UserBuilder users = User.withDefaultPasswordEncoder();

      auth.inMemoryAuthentication()

        .withUser(users.username("admin").password("1234").roles("USER"))

        .withUser(users.username("user").password("1234").roles("ADMIN"));

    */

    }

 

 

    @Override

    protected void configure(HttpSecurity http) throws Exception {


        http.authorizeRequests()

        

        // index

        .antMatchers("/")

            .permitAll()


         // 접근 오류

     .antMatchers("/member/accessDenied")

         .permitAll()

         

         .antMatchers("/member/accessDeniedView")

         .permitAll()    


         // 회원 로그인 기능

     .antMatchers("/member/loginForm")

         .permitAll()

         

     // 관리자 페이지 기능

        .antMatchers("/admin/**")

        .hasRole("ADMIN")

        // "RULE_ADMIN이라고 DB에 입력되어 있다면, RULE_은 제거하고 입력해야 인식함."

        

        // 폼 로그인 명세

        .and()

            .formLogin()

        .permitAll()

                .loginPage("/member/loginForm")

                .failureForwardUrl("/member/loginForm?error")

                .defaultSuccessUrl("/")

                .usernameParameter("id")

                .passwordParameter("password")

       

        // 로그아웃 처리

.and()

                .logout()

                .logoutUrl("/logout")

                .logoutSuccessUrl("/")

                .invalidateHttpSession(true)

                .deleteCookies("JSESSION_ID")

                .deleteCookies("remember-me")

        // 로그인 

            .and()

            .rememberMe()

            .tokenValiditySeconds(604800)

            .tokenRepository(persistentTokenRepository())

            // 예외처리(

            .and()

        .exceptionHandling()

        .accessDeniedPage("/member/accessDenied")

        // csrf 설정

            .and()

                .csrf().disable();

       

        

            

        

    /*

        http.authorizeRequests()

        .antMatchers("/login")

            .permitAll()

        .antMatchers("/**")

            .hasAnyRole("ADMIN", "USER")

            .hasAnyAuthority("RULE_ADMIN", "RULE_USER")

        .and()

            .formLogin()

            .loginPage("/login")

            .defaultSuccessUrl("/")

            .failureUrl("/login?error=true")

            .permitAll()

            

            .loginPage("/login")

            .usernameParameter("email")

            .passwordParameter("password")

            .successHandler(successHandler())      

            .failureHandler(failureHandler())

            .permitAll();

            

        .and()

            .logout()

            .logoutSuccessUrl("/login?logout=true")

            .invalidateHttpSession(true)

            .permitAll()

        .and()

            .csrf()

            .disable();

    */

        

    }

    

    // 로그아웃 Persistent_Logins에 관한 설정   (주석 해도 무방)...

    @Bean 

public PersistentTokenRepository persistentTokenRepository() {

JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();

DataSource usrDS = getDataSource();

db.setDataSource(usrDS);

return db;

}

    


    // DataSource 불러오기    (주석 해도 무방)

@Bean

public DataSource getDataSource() {

       // BasicDataSource dataSource = new BasicDataSource(); - Apache DBCP2

SqlMapSessionFactory factory = SqlMapSessionFactory.getInstance();

       return factory.getOracleDataSource(); // 오라클 적용함.

}

    

// 비밀번호 생성 - 암호(BCryptPasswordEncoder)

    @Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

        

    }

    

}


파일명: SecurityConfig.java


[첨부(Attachments)]

SecurityConfig.zip



비고: 주석 잘 쳐서 정리해서 빌드해보면, 내장 로그인 페이지를 볼 수 있다.
       - SecurityWebApplicationInitializer.java, SecurityConfig.java 두 개 파일의 힘이 얼마나 큰지 실감 해볼 수 있다.

         Spring-Framework 설정 전체를 제어해버린다고 해도 된다.




17. 로그인 인증 - Spring Security (CustomAuthenticationProvider.java)


이 코드 부분은 찾아보려고 해도 쉽게 나오지 않는다. 어려운 부분 중 하나이다.

공개하는 이유는 삽질을 적게 하라는 의미이다.


패키지 경로: com.springMVC.javaSecurity5.config


package com.springMVC.javaSecurity5.config;


import java.util.List;


import org.springframework.security.authentication.AuthenticationProvider;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.stereotype.Component;


import com.springMVC.javaSecurity5.service.CustomUserDetailsService;


@Component

public class CustomAuthenticationProvider implements AuthenticationProvider {

    

    private UserDetailsService userDeSer;

 

    @Override

    public Authentication authenticate(Authentication authentication) {

        

        String username = (String) authentication.getPrincipal();

        String password = (String) authentication.getCredentials();

        

        if ( username.equals("fail")) {

        System.out.println("(에러)아이디: 실패");

        return null;

        }

        

        // DB 정보 읽기

        userDeSer = new CustomUserDetailsService();

        UserDetails userDetail = userDeSer.loadUserByUsername(username);

        

        @SuppressWarnings("unchecked")

  List<GrantedAuthority> roles = (List<GrantedAuthority>) userDetail.getAuthorities();

        

        // 권한

        System.out.println("DB불러오기-권한:" + userDetail.getAuthorities());

        System.out.println("DB불러오기-비밀번호:" + userDetail.getPassword());

        System.out.println("roles:" + roles.get(0));

        

        if ( !matchPassword(password, userDetail.getPassword())) {

        System.out.println("(에러)비밀번호: 불일치" + password);

        return null;

        }

        

        UsernamePasswordAuthenticationToken result =

        new UsernamePasswordAuthenticationToken(username, password, roles);

        

        result.setDetails(userDetail);

        

        return result;

    }

 

    @Override

    public boolean supports(Class<?> authentication) {

        return true;

    }

    

    private boolean matchPassword(String loginPwd, String password) {

   

        BCryptPasswordEncoder secure = new BCryptPasswordEncoder();

        return secure.matches(loginPwd, password);

    }

 

}


파일명: CustomAuthenicationProvider.java


[첨부(Attachments)]

CustomAuthenticationProvider.zip


어디에 구체적으로 사용되는 부분인가?


사용하는 영역은 SecurityConfig.java에 http의 auth의 .authenicationProvider()에 사용된다.



그림 25. SecurityConfig.java


왜 이 코드를 사용하는지 소개해본다면,

"No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken"

이 문제가 디버그 오류창에 뜨는 것을 볼 수 있다.


기본 내장형 계정 생성 등으로 인증을 시도하면 xml방식에서는 처리해줬는데, java방식에서는 인증받지 못한다.


     /* 현재 - 임시


        auth.inMemoryAuthentication()

        .passwordEncoder(passwordEncoder)

        .withUser("user").password(passwordEncoder.encode("1234")).roles("RULE_USER")

        .and()

        .withUser("admin").password(passwordEncoder.encode("1234")).roles("RULE_USER", "RULE_ADMIN");

        

        */


[문제가 발생되는 기본형 - 코드]


이 코드로 작업하면, xml에서는 동작되었던 부분이 동작되질 않는다.


토큰 인증도 해결할 겸 "CustomAuthenicationProvider.java를 설계해서 문제를 해결한 것이다.




18. SQL (Factory) - SqlMapSessionFactory.java


나중에 CP(Connection Pool, 커넥션 풀)이라고 불리는 것으로 구현해봐도 좋을 듯 싶다.

iBatis를 남용해서 프로젝트에 기본마냥 소개하는 책들이 무척 많은데 기본은 순수한 DB를 사용하는 것부터 출발하는 것이다.


iBatis는 SQL 코드 개발 등에서 생산성이 좋아지는 도구 중 하나이지만, 필수 사항은 아니라고 본다.

차라리 필수 사항을 꼽아본다면, 커넥션 풀을 하나 추천해보고 싶다.


아무튼 커넥션 풀 주제가 아니기 때문에 생략한다.


패키지 경로: com.springMVC.javaSecurity5.db


package com.springMVC.javaSecurity5.db;


import java.io.IOException;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Properties;


import javax.sql.DataSource;


import oracle.jdbc.pool.OracleDataSource;


public class SqlMapSessionFactory {

private static SqlMapSessionFactory factory = new SqlMapSessionFactory();


private SqlMapSessionFactory() {}


private final String driverName = "oracle.jdbc.driver.OracleDriver";

private final String dbUrl = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";

private final String userName = "{사용자계정명}";

private final String userPassword = "{비밀번호}";

public static SqlMapSessionFactory getInstance() {

return factory;

}


/*

*     public static DataSource getMySQLDataSource() {

        Properties props = new Properties();

        FileInputStream fis = null;

        MysqlDataSource mysqlDS = null;

        try {

            fis = new FileInputStream("db.properties");

            props.load(fis);

            mysqlDS = new MysqlDataSource();

            mysqlDS.setURL(props.getProperty("MYSQL_DB_URL"));

            mysqlDS.setUser(props.getProperty("MYSQL_DB_USERNAME"));

            mysqlDS.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));

        } catch (IOException e) {

            e.printStackTrace();

        }

        return mysqlDS;

    }

    */

    public DataSource getOracleDataSource(){

        

    OracleDataSource oracleDS = null;

        

    try {

            oracleDS = new OracleDataSource();

            oracleDS.setURL(dbUrl);

            oracleDS.setUser(userName);

            oracleDS.setPassword(userPassword);

        } catch (SQLException e) {

            e.printStackTrace();

        }

        return oracleDS;

        

    }

public Connection connect() {


Connection conn = null;


try {

Class.forName(driverName);

conn = DriverManager.getConnection(dbUrl, userName, userPassword);

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}


return conn;


}


public void close(Connection conn, PreparedStatement ps, ResultSet rs) {


if ( rs != null ) {


try {

rs.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}


close(conn, ps); // Recursive 구조 응용(재귀 함수)


} // end of if


}


public void close(Connection conn, PreparedStatement ps) {


if (ps != null ) {

try {

ps.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

} // end of if


if (conn != null ) {

try {

conn.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

} // end of if


}

}



파일명: SqlMapSessionFactory.java


[첨부(Attachments)]

SqlMapSessionFactory.zip



비고: 재사용이 가능한 형태로 설계하였다.



19. Model - CustomUserDetails.java


순수한 Model 형태는 아니고, 부분 개량하였다.

Spring-Security에서 제공하는 UserDetails(인터페이스)를 Model 클래스에 구현해야 한다.

List<role> 기능 문제 등으로 인해서 String authorities를 List<GrantedAuthority>로 변경하였다.


@Override 된 부분들이 UserDetails에 정의된 내용이다.


패키지 경로: com.springMVC.javaSecurity5.model


package com.springMVC.javaSecurity5.model;


import java.util.ArrayList;

import java.util.Collection;

import java.util.List;


import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;


public class CustomUserDetails implements UserDetails{


private static final long serialVersionUID = 1L;

private String username;

    private String password;

    // 개량함. (다중 권한 고려)

    private List<GrantedAuthority> authorities;

    private boolean enabled;

        

public String getUsername() {

return username;

}


public void setUsername(String username) {

this.username = username;

}


public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

     public void setAuthority(String authority) {

// 권한 객체 생성

if ( authorities == null ) {

authorities = new ArrayList<GrantedAuthority>();

}

// 권한 추가

SimpleGrantedAuthority grantObj = new SimpleGrantedAuthority(authority);

authorities.add(grantObj);

     }


public boolean getEnabled() {

return enabled;

}

public void setEnabled(boolean enabled) {

this.enabled = enabled;

}


@Override

public Collection<? extends GrantedAuthority> getAuthorities() {


        return authorities;


}


@Override

public boolean isAccountNonExpired() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isAccountNonLocked() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isCredentialsNonExpired() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isEnabled() {

// TODO Auto-generated method stub

return false;

}

    

}


파일명: CustomUserDetails.java


[첨부(Attachments)]

CustomUserDetails.zip





20. Service - CustomUserDetailsService.java


CustomUserDetailsService는 Spring-Security의 "UserDetailsService(인터페이스)"로 정의된 내용을 구현하는 것이다.

인터페이스의 영향도 있지만, DB를 실제로 불러올 때 사용자 관점에서 처리되는 부분이라고 본다.


package com.springMVC.javaSecurity5.service;


import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.core.userdetails.UsernameNotFoundException;


import com.springMVC.javaSecurity5.dao.SqlSessionTemplate;

import com.springMVC.javaSecurity5.model.CustomUserDetails;


public class CustomUserDetailsService implements UserDetailsService {

    

    private SqlSessionTemplate sqlSession = SqlSessionTemplate.getInstance();

 

    public CustomUserDetails getUserById(String username) {

        return sqlSession.selectOne("user.selectUserById", username);

    }

 

    public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {


    CustomUserDetails user = sqlSession.selectOne("null", username);

        

        if(user==null) {

            throw new UsernameNotFoundException(username);

        }

        return user;

    }

    

}


파일명: CustomUserDetailsService.java


[첨부(Attachments)]

CustomUserDetailsService.zip




21. DAO - SqlSessionTemplate.java


실제 DB를 구현하는 부분이다.


package com.springMVC.javaSecurity5.dao;


import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;


import com.springMVC.javaSecurity5.db.SqlMapSessionFactory;

import com.springMVC.javaSecurity5.model.CustomUserDetails;


public class SqlSessionTemplate {


private SqlSessionTemplate() {}

private static SqlSessionTemplate sqlTemplate;

    private static SqlMapSessionFactory session; 

    

    public static SqlSessionTemplate getInstance(){

   

        if(sqlTemplate == null){

        sqlTemplate = new SqlSessionTemplate();

            session = SqlMapSessionFactory.getInstance();

        }


        return sqlTemplate;

    }

    

// 추후 iBatis 고려

public CustomUserDetails selectOne(String id, String username) {

Connection conn = null;

    PreparedStatement pstmt = null;

    ResultSet rs = null;

   

    CustomUserDetails node = null;


    String sql = "select g1.username, g1.password, g2.authority, " + 

       "g1.enabled from comp_users g1, comp_authorities g2 where g1.username = g2.username " +

       "and g1.username = ?";


    System.out.println(sql);


    try {


    conn = session.connect();


    pstmt = conn.prepareStatement(sql);

    pstmt.setString(1, username);

   

    rs = pstmt.executeQuery();


    while ( rs.next() ) {

   

    // 데이터가 존재할 때, 노드 생성

    node = new CustomUserDetails();

    node.setUsername(rs.getNString(1));

    node.setPassword(rs.getNString(2));

    node.setAuthority(rs.getNString(3));

    System.out.println("rs:" + rs.getNString(3));

    node.setEnabled(rs.getBoolean(4));

    }


    }catch(Exception ex) {

    System.out.println("오류 발생: " + ex);

    }

    finally {

    session.close(conn, pstmt, rs);

    }


    return node;

}

}



파일명: SqlSessionTemplate.java


[첨부(Attachments)]

SqlSessionTemplate.zip



* 3부에서는 View에 대해서 구현하는 방법을 소개하겠다.


3부에서는 "jsp 파일" 등 사용자 인터페이스 화면에 대해서 소개하겠다.


1. [Spring-Framework] 17. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (3), 2020-09-27

https://yyman.tistory.com/1424


반응형
728x90
300x250

[Spring-Framework] 5. 순정 Maven MVC 웹 프로젝트 - 서블릿, Oracle DB


이번에 소개할 프로젝트는 Maven을 활용하여 Oracle 연동에 대해서 소개하려고 한다.


* 미리 준비해야 할 것: Oracle Database 설치되어 있어야 함.

-> 오라클의 경우에는 오라클 데이터베이스가 설치되어야만, pom.xml에서 <dependency>로 불러올 수가 있음.



[글의 주제]

1. 흥미로운 실험 - Spring Framework가 없는 Maven MVC 웹 프로젝트로 Servlet(서블릿) 구성해보기

2. Oracle Database 연결해보기


글의 주제를 2가지로 설정한 이유는 Spring Framework와 Maven을 동일시하는 경우가 있어서 차이를 알아보기 위해서 작성하게 되었다.


[사용 환경]

1. Oracle Databases 19g (2020-09월 기준: 최신버전)

2. OpenJDK (2020-09월 기준: 최신버전)

3. Apache Tomcat 9

4. SpringToolSuite4 (2020-09월 기준: 최신버전)

5. MS Windows 10



1. MVNRepository가 만능인가?


몇 가지 미지원하는 제품도 존재할 수 있다. 참고하면 도움이 될 것이다.



그림 1) MVNRepository 검색하기


https://mvnrepository.com/


접속하여 "Oracle"이라고 검색한다.

Oracle JDBC 를 클릭한다.

최신버전을 선택해본다.



그림 2) MVNRepository - Oracle JDBC 12.1.0.2


Maven 생성 코드가 나왔다.

해당 코드를 복사, 붙여넣기를 pom.xml에 시도 해본다.



그림 3) pom.xml에 적용했을 때 오류


Multiple problems have occur......

Error reading file - oracle-jdbc-12.1.0.2.jar 파일을 읽어올 수 없다고 에러가 발생한다.


이렇게 에러가 출력되었다면, 정상적으로 코드를 입력한 것이다.

오류가 발생하는 원인으로는 오라클 데이터베이스는 상용 소프트웨어이기 때문이다.



2. 프로젝트에서 Maven -> Add Dependency 기능 활용하기


Maven 플러그인의 Add Dependency 기능으로 적용해보려고 한다.



그림 4) Maven의 프로젝트


작업중인 프로젝트를 클릭한다.

오른쪽 버튼을 누른다.

"Maven"-> "Add Dependency"를 클릭한다.



그림 5) Maven의 프로젝트


oracle을 검색한다.

com.oracle.database.jdbc | ojdbc8을 선택한 후 OK를 누른다.



그림 6) Maven의 프로젝트


자동으로 oracle jar파일이 생성되는 것을 확인할 수 있다.

버전을 자세히 보면, 19.3.0.0이라고 적혀져 있다.

오라클 최신 버전이 컴퓨터 내에 설치되어 있어서, 해당 버전을 인식하여 가져온 것으로 보인다.




3. Maven에서의 Java 코드 생성하기


이전의 프로젝트에 비해서 복잡하게 느껴질 수도 있지만, 큰 차이점은 없다고 보면 되겠다.

먼저 AddressDto라는 클래스를 생성해보려고 한다.



그림 7) Class 생성하기


Java Resources에서 오른쪽 버튼을 누른다.

New 항목에서 Class를 클릭한다.



그림 8) AddressDto.java 파일 만들기


Package명을 간단하게 "mavenWeb.db"로 입력하였다.

Name명은 "AddressDto"라고 입력하였다.

다 입력하였으면, "Finish"를 누른다.



그림 9) 코드를 입력한 모습 - AddressDto.java


크게 어렵지 않게 Jsp 프로그래밍에서의 Java 파일 생성한 방법처럼, 동일한 형태로 사용할 수 있다는 것을 알 수 있다.





4. Maven에서의 Servlet 코드 생성하기


이전의 서블릿 작업은 web.xml에서 수정작업을 추가적으로 해줘야만 했었다.

Maven에서 서블릿(Servlet)을 생성하면, 자동으로 web.xml에 입력된다.



그림 10) Java Resources의 오른쪽 버튼 메뉴 모습


Java Resources를 선택한 후 오른쪽 버튼 클릭하여 New->Servlet을 클릭한다.



그림 11) Create Servlet


Java Package명은 "mavenWeb.view"으로 지정하였다.

Class name명은 "BoardListServlet"으로 지정하였다.

완료되었다면, Finish를 누른다.



그림 12) web.xml 파일


파일경로: src/main/webapp/WEB-INF/web.xml


자동으로 <Servlet>과 <Servlet-Mapping>이 등록된 것을 볼 수 있다.



그림 13) BoardListServlet.java 파일


doGet함수와 doPost 등이 자동 생성된 프로그램을 볼 수 있다.

Run을 하여 "아파치 톰캣 서버"를 동작시킨 후 아래처럼 태스트를 해볼 수 있다.



그림 14) Open Browser에서의 서블릿 동작 확인하기


매우 친숙한 화면에서 동작되는 모습을 확인할 수 있다.




5. Oracle DB - 간단하게 연동시키기(프레임워크 X - 순정 JDBC) [소스코드]


JDBC 기반으로 Maven Servlet 프로젝트 작업을 구현하겠다.



그림 15) 프로젝트 구성하기의 예


작업해줄 영역은 크게 mavenWeb.db, mavenWeb.view, WEB-INF내의 web.xml 파일이 되겠다.


* 이전 게시글(동일하거나 참고하면 되는 게시글):

1. [JSP] 영속프레임워크 MyBatis를 활용한 CRUD 구현 - JSP와 Oracle, https://yyman.tistory.com/1390?category=810693, 2020-09-19 01:31

-> 비교해서 읽어 보기: 현재 게시글에서는 프레임워크 없이 JDBC 처리에 대해서 소개하고 있음.




package mavenWeb.db;


import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;


public class SqlMapSessionFactory {


private static SqlMapSessionFactory factory = new SqlMapSessionFactory();

private SqlMapSessionFactory() {}

public static SqlMapSessionFactory getInstance() {

return factory;

}

public Connection connect() {

Connection conn = null;

try {

Class.forName("oracle.jdbc.driver.OracleDriver");

conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl", "사용자명", "비밀번호");

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

return conn;

}

public void close(Connection conn, PreparedStatement ps, ResultSet rs) {

if ( rs != null ) {

try {

rs.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

close(conn, ps); // Recursive 구조 응용(재귀 함수)

}

}

public void close(Connection conn, PreparedStatement ps) {

if (ps != null ) {

try {

ps.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

}

if (conn != null ) {

try {

conn.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

}

}

}



* 파일명: SqlMapSessionFactory.java


[첨부(Attachments)]

SqlMapSessionFactory.zip


package mavenWeb.db;


import java.sql.Date;


public class AddressDto {


private int num;

private String name;

private String address;

private Date birthdate;

public int getNum() {

return num;

}

public void setNum(int num) {

this.num = num;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

public Date getBirthdate() {

return birthdate;

}

public void setBirthdate(Date birthdate) {

this.birthdate = birthdate;

}


}



* 파일명: AddressDto.java


비고: 이전 프로젝트와 동일함. (MyBatis 게시글과 흡사함)

java.sql.Timestamp에서 java.sql.Date로 변경함.


[첨부(Attachments)]

AddressDto.zip


package mavenWeb.db;


public interface Address {


public AddressDto getAddress(Integer num);

public int updateAddress(AddressDto addressDTO);

public int insertAddress(AddressDto addressDTO); 

public int deleteAddress(Integer num);

}


* 파일명: Address.java (인터페이스)


비고: 이전 프로젝트와 동일함. (MyBatis 게시글과 동일함)


[첨부(Attachments)]

Address.zip


package mavenWeb.db;


public class AddressImpl implements Address {


@Override

public AddressDto getAddress(Integer num) {

AddressDao dao = AddressDao.getInstance();

return dao.selectAddress(num);

}


@Override

public int updateAddress(AddressDto addressDTO) {

AddressDao dao = AddressDao.getInstance();

return dao.updateAddress(addressDTO);

}


@Override

public int insertAddress(AddressDto addressDTO) {

AddressDao dao = AddressDao.getInstance();

return dao.insertAddress(addressDTO);

}


@Override

public int deleteAddress(Integer num) {

AddressDao dao = AddressDao.getInstance();

return dao.deleteAddress(num);

}


}



* 파일명: AddressImpl.java


비고: 이전 프로젝트와 동일함. (MyBatis 게시글과 동일함)


[첨부(Attachments)]

AddressImpl.zip


package mavenWeb.db;


import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.text.SimpleDateFormat;

import java.util.Calendar;


public class AddressDao {

private AddressDao() {}

    private static AddressDao dao;

    private static SqlMapSessionFactory session; 


    public static AddressDao getInstance(){


        if(dao == null){

            dao = new AddressDao();

            session = SqlMapSessionFactory.getInstance();

        }


        return dao;

    }

    


    public AddressDto selectAddress(Integer num) {


    Connection conn = null;

    PreparedStatement pstmt = null;

    ResultSet rs = null;

   

    AddressDto node = new AddressDto();

   

    String sql = "select NUM, NAME, ADDRESS, BIRTHDATE " +

      " from addressbook" + 

      " where num=?";

    System.out.println(sql);

   

    // 달력 날짜 출력 버그 개선

   

    try {

    conn = session.connect();

    pstmt = conn.prepareStatement(sql);

    pstmt.setInt(1, num);

   

    rs = pstmt.executeQuery();

   

    if ( rs.next() ) {

    node.setNum(rs.getInt(1));

    node.setName(rs.getNString(2));

    node.setAddress(rs.getNString(3));

    node.setBirthdate(rs.getDate(4));

    }

   

   

    }catch(Exception ex) {

    System.out.println("오류 발생: " + ex);

    }

    finally {

    session.close(conn, pstmt, rs);

    }

   

        return node;


    }

    

    public int updateAddress(AddressDto addressDTO) {


    Connection conn = null;

    PreparedStatement pstmt = null;

    int result = -1;

   

    String sql = "update addressbook set NAME = ?, ADDRESS = ?, BIRTHDATE = ? " + 

      " where num = ?";

    try {

   

    System.out.println(addressDTO.getBirthdate());

   

    conn = session.connect();

    pstmt = conn.prepareStatement(sql);

    pstmt.setString(1, addressDTO.getName());

    pstmt.setString(2, addressDTO.getAddress());

    pstmt.setDate(3, addressDTO.getBirthdate());

    pstmt.setInt(4,  addressDTO.getNum());

   

    result = pstmt.executeUpdate();

   

   

    }catch(Exception ex) {

    System.out.println("오류 발생: " + ex);

    }

    finally {

    session.close(conn, pstmt);

    }

   

    return result;

   

    }


    public int insertAddress(AddressDto addressDTO) {

   

    Connection conn = null;

    PreparedStatement pstmt = null;

    int result = -1;

   

    String sql = "insert into addressbook (NAME, ADDRESS, BIRTHDATE) " + 

      " values(?,?,?)";

    try {

   

    System.out.println(addressDTO.getBirthdate());

   

    conn = session.connect();

    pstmt = conn.prepareStatement(sql);

    pstmt.setString(1, addressDTO.getName());

    pstmt.setString(2, addressDTO.getAddress());

    pstmt.setDate(3, addressDTO.getBirthdate());

   

    result = pstmt.executeUpdate();

   

   

    }catch(Exception ex) {

    System.out.println("오류 발생: " + ex);

    }

    finally {

    session.close(conn, pstmt);

    }

   

    return result;

    }

    

    public int deleteAddress(Integer num) {

   

    Connection conn = null;

    PreparedStatement pstmt = null;

    int result = -1;

   

    String sql = "delete from addressbook " + 

      " where num = ?";

    try {

   

    conn = session.connect();

    pstmt = conn.prepareStatement(sql);

    pstmt.setInt(1, num);

   

    result = pstmt.executeUpdate();

   

   

    }catch(Exception ex) {

    System.out.println("오류 발생: " + ex);

    }

    finally {

    session.close(conn, pstmt);

    }

   

    return result;

    }


    

}



* 파일명: AddressDao.java


비고: 


[첨부(Attachments)]

AddressDao.zip





6. 서블릿 뷰 - [소스코드]


web.xml을 살짝만 수정해주면 된다. 크게 많이 수정할 필요가 없다.

이유는 자동생성되기 때문이다.


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


<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

version="3.1">

  <display-name>Archetype Created Web Application</display-name>

  

  

<!-- Subject: web.xml     -->

<!-- Filename: web.xml     -->

<!-- Created Date: 2020-09-19     -->

<!-- * Description:     --> 

<!-- 1. Maven 기반의 Servlet 실험(2020-09-20) --> 

  

  <welcome-file-list>

<welcome-file>index.html</welcome-file>

<welcome-file>index.htm</welcome-file>

<welcome-file>index.jsp</welcome-file>

<welcome-file>default.html</welcome-file>

<welcome-file>default.htm</welcome-file>

<welcome-file>default.jsp</welcome-file>

  </welcome-file-list>

  <!-- Board Servlet 자동생성됨 -->

  <servlet>

  <servlet-name>BoardListServlet</servlet-name>

  <servlet-class>mavenWeb.view.BoardListServlet</servlet-class>

  </servlet>

  <servlet>

  <servlet-name>BoardInsertServlet</servlet-name>

  <servlet-class>mavenWeb.view.BoardInsertServlet</servlet-class>

  </servlet>

  <servlet>

  <servlet-name>BoardDeleteServlet</servlet-name>

  <servlet-class>mavenWeb.view.BoardDeleteServlet</servlet-class>

  </servlet>

  <servlet>

  <servlet-name>BoardUpdateServlet</servlet-name>

  <servlet-class>mavenWeb.view.BoardUpdateServlet</servlet-class>

  </servlet>

  <servlet-mapping>

  <servlet-name>BoardListServlet</servlet-name>

  <url-pattern>/board/list.do</url-pattern>

  </servlet-mapping>

  <servlet-mapping>

  <servlet-name>BoardInsertServlet</servlet-name>

  <url-pattern>/board/insert.do</url-pattern>

  </servlet-mapping>

  <servlet-mapping>

  <servlet-name>BoardDeleteServlet</servlet-name>

  <url-pattern>/board/delete.do</url-pattern>

  </servlet-mapping>

  <servlet-mapping>

  <servlet-name>BoardUpdateServlet</servlet-name>

  <url-pattern>/board/update.do</url-pattern>

  </servlet-mapping>


</web-app>


* 파일명: web.xml


[첨부(Attachments)]

web.zip


package mavenWeb.view;


import java.io.IOException;

import java.io.PrintWriter;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import mavenWeb.db.AddressDto;

import mavenWeb.db.AddressImpl;


/**

 * Servlet implementation class BoardDeleteServlet

 */

public class BoardDeleteServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    /**

     * @see HttpServlet#HttpServlet()

     */

    public BoardDeleteServlet() {

        super();

    }


/**

* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

*/

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.print("<br/>");

AddressImpl address = new AddressImpl();

int result = address.deleteAddress(5);

AddressDto addressDto = address.getAddress(3);

out.println("<html><head><title>CRUD - Delete(Maven)</title></head>");

out.println("<body><h2>MyBatis - Delete(Maven)</h2>");

out.print("<br/>");

out.print("삭제여부:" + result + "</br>");

out.print("<br/>");

if ( addressDto != null ) {

out.print(addressDto.getNum() + "/" + addressDto.getName() + "/");

out.print(addressDto.getAddress() + "/" + addressDto.getBirthdate());

}

out.println("</body></html>");

out.close();

}


/**

* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

*/

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doGet(req, res);

}


}



* 파일명: BoardDeleteServlet.java


[첨부(Attachments)]

BoardDeleteServlet.zip


package mavenWeb.view;


import java.io.IOException;

import java.io.PrintWriter;

import java.sql.Date;

import java.text.SimpleDateFormat;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import mavenWeb.db.AddressDto;

import mavenWeb.db.AddressImpl;


/**

 * Servlet implementation class BoardInsertServlet

 */

public class BoardInsertServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    /**

     * @see HttpServlet#HttpServlet()

     */

    public BoardInsertServlet() {

        super();

    }


/**

* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

*/

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.print("<br/>");

AddressImpl address = new AddressImpl();

AddressDto dbNode = new AddressDto(); 

dbNode.setName("도도" + serialVersionUID);

dbNode.setAddress("행복시 행복동");

// 버그1: new Date() 사용안됨. (2020을 3920으로 인식함.) 

// 버그2: new Timestamp() 사용안됨. (2020을 3920으로 인식함.) 

String userDate = "2020-02-01";

java.sql.Date sqlDate = java.sql.Date.valueOf(userDate);

dbNode.setBirthdate(sqlDate);

int result = address.insertAddress(dbNode);

AddressDto addressDto = address.getAddress(1);

out.println("<html><head><title>CRUD - Insert(Maven)</title></head>");

out.println("<body><h2>MyBatis - Insert</h2>");


SimpleDateFormat format1 = new SimpleDateFormat ( "yyyy-MM-dd" );

String birthdate = format1.format(addressDto.getBirthdate());

out.print("<br/>");

out.print("등록여부:" + result + "</br>");

out.print("<br/>");

out.print(addressDto.getNum() + "/" + addressDto.getName() + "/");

out.print(addressDto.getAddress() + "/" + birthdate);

out.println("</body></html>");

out.close();

}


/**

* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

*/

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

// TODO Auto-generated method stub

doGet(request, response);

}


}



* 파일명: BoardInsertServlet.java


[첨부(Attachments)]

BoardInsertServlet.zip


package mavenWeb.view;


import java.io.IOException;

import java.io.PrintWriter;

import java.text.SimpleDateFormat;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import mavenWeb.db.AddressDto;

import mavenWeb.db.AddressImpl;


/**

 * Servlet implementation class BoardListServlet

 */

public class BoardListServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    /**

     * @see HttpServlet#HttpServlet()

     */

    public BoardListServlet() {

        super();

    }


/**

* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

*/

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.print("<br/>");

AddressImpl address = new AddressImpl();

AddressDto addressDto = address.getAddress(16);

out.println("<html><head><title>CRUD - List(Maven)</title></head>");

out.println("<body><h2>MyBatis - List</h2>");

SimpleDateFormat format1 = new SimpleDateFormat ( "yyyy-MM-dd" );

String birthdate = format1.format(addressDto.getBirthdate());

out.print(addressDto.getNum() + "/" + addressDto.getName() + "/");

out.print(addressDto.getAddress() + "/" + birthdate);

out.print("<br/>");

out.println("</body></html>");

out.close();

}


/**

* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

*/

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doGet(req, res);

}


}



* 파일명: BoardListServlet.java


[첨부(Attachments)]

BoardListServlet.zip


package mavenWeb.view;


import java.io.IOException;

import java.io.PrintWriter;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import mavenWeb.db.AddressDto;

import mavenWeb.db.AddressImpl;


/**

 * Servlet implementation class BoardUpdateServlet

 */

public class BoardUpdateServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    /**

     * @see HttpServlet#HttpServlet()

     */

    public BoardUpdateServlet() {

        super();

    }


/**

* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

*/

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.print("<br/>");

AddressImpl address = new AddressImpl();

AddressDto dbNode = new AddressDto();

dbNode.setNum(3);

dbNode.setName("도도수정" + serialVersionUID);

dbNode.setAddress("행복시 행복동");


// 버그1: new Date() 사용안됨. (2020을 3920으로 인식함.) 

// 버그2: new Timestamp() 사용안됨. (2020을 3920으로 인식함.)

String userDate = "2020-07-01";

java.sql.Date sqlDate = java.sql.Date.valueOf(userDate);

dbNode.setBirthdate(sqlDate);

int result = address.updateAddress(dbNode);

AddressDto addressDto = address.getAddress(3);

out.println("<html><head><title>CRUD - Update(Maven)</title></head>");

out.println("<body><h2>MyBatis - Update(Maven)</h2>");

out.print("<br/>");

out.print("수정여부:" + result + "</br>");

out.print("<br/>");

out.print(addressDto.getNum() + "/" + addressDto.getName() + "/");

out.print(addressDto.getAddress() + "/" + addressDto.getBirthdate());

out.println("</body></html>");

out.close();

}


/**

* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

*/

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doGet(req, res);

}


}



* 파일명: BoardUpdateServlet.java


[첨부(Attachments)]

BoardUpdateServlet.zip




7. 데이터베이스 설계


예제 데이터베이스를 설계하도록 하겠다. 역설계에 대해서도 생각해보면, 흥미로운 주제가 될 것으로 보인다.


tableName(테이블명): addressbook


 키

 항목명

 속성 

 PK(기본키)

num

인덱스(ID)

 

name

nvarchar

 

address

nvarchar

 

birthdate

date




그림 16) 데이터베이스 테이블 - 설계



8. 결론(Conclusion)


순정 Maven MVC의 사용방법은 JSP/Servlet하고 동일하거나 큰 차이가 없다는 점을 알 수 있었다.

Spring-Framework의 Maven을 사용할 때 기본 원리를 이해하는 데 도움될 수 있을 거 같다.


* 소스코드: 동작 태스트 완료하였음.


Maven을 사용하면, 편해지는 부분이 정말 많아진다.



* 참고자료(Reference)


1. 자바 오라클 연동 데이터 삽입,수정,조회,삭제-1, https://ngg3319.tistory.com/76, Accessed by 2020-09-20, Last Modified 2018-03-12 16:17

2. 자바 오라클 연동 데이터 삽입,수정,조회,삭제-2, https://ngg3319.tistory.com/79, Accessed by 2020-09-20, Last Modified 2018-03-13 22:02

-> 추천(40점): 의외로 쉽게 잘 작성되어 있음.

-> 참고 부분: sqlDate 에 대해서 처리하는 방법 참고함. (String 문자로 java.sql.Date.valueOf()의 아이디어를 얻었음.)


3. maven 이용 spring MVC project 생성 :: Copy Coding, https://copycoding.tistory.com/178, Accessed by 2020-09-20, Last Modified 2019-05-03 12:27

-> 추천(70점): 순수한 Maven 프로젝트에 대해서 잘 설명되어 있음.


반응형

+ Recent posts