10. Spring/MVC

01. Spring MVC - Pom.xml + MariaDB + Mybatis + 네이버클라우드플랫폼

THE HEYDAZE 2020. 6. 12. 03:57
OS Windows 10 Home 64bit 버전 1903 (OS 빌드 18362.836)
Edit Tool IntelliJ 2019.1.3
Build Tool Maven
DataBase MariaDB
FrameWork Spring MVC
Server Side Language jsp
Cloud Server Naver Cloud Platform
#1. 프로젝트 생성

#2. Maven 설정

pom.xml
0.01MB

버전 설정

(라이브러리 버전 관리를 쉽게하기 위해 변수처럼 선언함)

1
2
3
4
5
<!-- Spring Version -->
<properties>
  <org.springframework-version>5.0.8.RELEASE</org.springframework-version>
</properties>
 
cs

 

Spring 설정

(@RequestMapping / @GetMapping / @Controller  / @Component / @Service @Repository)

1
2
3
4
5
6
7
<!-- SpringWebMVC  -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>${org.springframework-version}</version>
</dependency>
 
cs

 

JSP 설정

(EL 태그 / JSTL 사용을 위한 라이브러리 <%%>, ${}, <jsp:include />, <c:if />, <fmt:formatNumber /> )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 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>
 
cs

 

Build 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<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.8</source>
                <target>1.8</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>
 
cs

 

#3. Spring MVC 디렉토리 구조 설정

이미지 클릭

 

이미지 클릭

#4. Web.xml 설정

web.xml
0.00MB

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?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_4_0.xsd"
         version="4.0">
 
    <display-name>Archetype Created Web Application</display-name>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value> <!-- root-Context -->
    </context-param>
 
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
 
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> <!-- servlet-Context  -->
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
 
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
</web-app>
cs

 

#5. servlet-Context 설정

home.jsp 생성

이미지 클릭

 

HomeController.java 생성

이미지 클릭

 

dispatcher-servlet.xml (context-Servlet) 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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 https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
 
    <annotation-driven />
 
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>
 
    <context:component-scan base-package="com.example.demo" />
 
</beans:beans>
 
cs

 

Tomcat 설정

이미지 클릭

 
톰캣 한글 인코딩   VM Options   -Dfile.encoding=UTF-8
톰캣 수정내용 반영
( Tomcat - Redeploy )
  On Update action   Update classes and resources
  On frame deactivation   Update classes and resources

 

구동

이미지 클릭

index.jsp 는 URL 주소로 입력하여 들어갈 수 있지만,

WEB-INF 경로에 있는 파일들은 URL 주소를 입력하여 들어갈 수 없다.

 

#6. 서버 설정

MariaDB 직접 설치 시 외부접속 허용 설정을 해주어야함

 

#7. 데이터베이스 연결

이미지 클릭

 

#8. 테이블 생성 + 입력

 

이미지 클릭

alt + insert = 삽입

 

이미지 클릭

alt + insert = 삽입

#9. Domain생성

이미지 클릭

 

#10. Controller 생성

이미지 클릭

 

#11. Service 생성

이미지 클릭

 

#12. DAO 생성

[필요 의존성]

(SqlSession / SqlSessionTemplate /SqlSessionFactory / Mapper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- org.mybatis/mybatis -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.4.1</version>
</dependency>
 
<!-- mybatis-spring -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>1.3.0</version>
</dependency>
 
cs

 

이미지 클릭

 

#13. Mapper 생성

테이블명 대소문자 구분

CDATA

alias

resultMap

${} / #{}

 

#14. Mybatis + Mapper Config 설정

이미지 클릭

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<!-- MapperConfig.xml -->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 
<configuration>
 
    <settings>
        <setting name="cacheEnabled" value="false"/>
        <setting name="useGeneratedKeys" value="true"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="jdbcTypeForNull" value="NULL" />
    </settings>
 
</configuration>
 
cs

 

#15. root-Context 설정

[필요 의존성]

(DB 접속을 위한 라이브러리 - DataSource / Connection / ResultSet)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- Spring-jdbc -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>${org.springframework-version}</version>
</dependency>
 
<!-- commons DBCP 커넥션 풀 -->
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.3</version>
</dependency>
 
<!-- MariaDB JDBC -->
<dependency>
  <groupId>org.mariadb.jdbc</groupId>
  <artifactId>mariadb-java-client</artifactId>
  <version>2.3.0</version>
</dependency>
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- DBCP Setting -->
    <bean id="dataSource"
          class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName"
                  value="org.mariadb.jdbc.Driver" />                  <!-- DataBase Driver -->
        <property name="url" <!-- DataBase URL -->
                  value="jdbc:mariadb://[서버아이피]:3306/[스키마]?autoReconnect=true&amp;useSSL=false" />
        <property name="username" value="[DB 아이디]" />               <!-- DataBase ID -->
        <property name="password" value="[비밀번호]" />                <!-- DataBase Password -->
    </bean>
 
    <!-- SqlSessionFactory 객체 주입 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- mybatis-config.xml -->
        <property name="mapperLocations">
            <list>
                <value>classpath:mappers/**/*Mapper.xml</value>                <!-- *Mapper.xml -->
            </list>
        </property>
    </bean>
 
    <!-- SqlSession 객체 주입 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    
 
    <!-- @Component Scan -->
    <context:component-scan base-package="com.example.demo.service"/> <!-- Service -->
    <context:component-scan base-package="com.example.demo.dao"/>     <!-- Repository -->
 
</beans>
 
cs
 
  org.mariadb.jdbc.Driver   Maria DB
  jdbc:mariadb://127.0.0.1:3306/test
  ?autoReconnect=true&amp;useSSL=false
  DB 접속 URL
  name="username" value="root"   DB 아이디
  name="password" value="●●"   DB 비밀번호
  value="classpath:mybatis-config.xml"   mybatis-config.xml 경로
  <value>classpath:mappers/**/*Mapper.xml<value>   Mapper.xml 경로
** 와 *Mapper.xml = mappers 하위폴더 전체에서 *Mapper.xml 로 끝나는 파일들을 리스트로 한다.

 

SpringBoot 에서는 application.properties 를 사용하거나 application.yml 로 설정한다.

 

jdbc:mariadb://127.0.0.1:3306/test?autoReconnect=true&useSSL=false 

인데 xml 에서 & 안써져서 &amp; 사용하였습니다

 

autoReconnect=true

[출처] https://kjk3071.tistory.com/entry/DB-MySQL-autoReconnecttrue

 

useSSL=false

SSL 의 강제성 때문에 각종 오류가 발생하는 것을 방지하기 위해서 SSL 사용하지 않음

 

#16. detail.jsp 생성 

 

#17. 로그인

1. home.jsp 수정

이미지 클릭

 

2. MemberController 수정

이미지 클릭

 

3. Member Alias

이미지 클릭

 

4. memberMapper.xml 수정

이미지 클릭

 

5. 로그인

이미지 클릭

 

6. get 접속불가

이미지 클릭

 

7. 세션

이미지 클릭

 

8. sessionScope 를 쓰는 이유

이미지 클릭

defaultScope 가 requestScope 이기 때문에

이전 페이지에서 post로 보낸 값들을 나타냄

 

DB 에서 가져온 데이터를 session에 주었기 때문에

sessionScope 으로 써주어야함

 

9. 세션 확인

이미지 클릭

 

10. 비회원 로그인 임시로 막기

이미지 클릭

DB에 없는 비회원도 detail.jsp 페이지로 이동하기 때문에

비회원일 경우 이전 페이지로 이동하는 script 문 작성함

 

alert();가  history.back(); 위에 있는 경우

alert 창을 닫아야지만 이전페이지로 이동함

 

alert();가 history.back(); 아래 있는 경우

alert 창이 뜨자마자 이전페이지로 이동함

 

11. 로그아웃

이미지 클릭

 

#18. 회원가입

1. GetMapping + 페이지 생성

이미지 클릭


2. PostMapping 메소드 생성

이미지 클릭


3. mapper 생성

이미지 클릭


4. 페이지 연결 및 회원가입

이미지 클릭

 


5. home.jsp 수정

이미지 클릭


6. 데이터베이스 Unique

Javascript 로 유효성검사는 사용자의 편의성을 위해 작성되는 것이고,

실질적으로는 서버에서 잘못된 데이터 또는 중복된 데이터 입력을 막아주여야 한다

 

이미지 클릭

위 와같이 중복된 ID와 입력하지 않은 값들에 대해 막아야 한다


+ 데이터 베이스 초기 설정 시 Not Null 로 설정하였지만,

빈 값이 존재하는 이유는 null 과 "" 는 다른 의미이기 때문이다

Spring 에서 매개변수들은 자동으로 객체를 생성해주는데

해당 객체의 멤버변수와 일치하는 요청값이 없다면

int 는 0

Integer, Long 은 null 

그리고 

String 은 "" 을 대입해준다.

"" 은 null 이 아닌 empty 라는 값이기 때문에 DB에 Commit 이 된다.

(아래 그림 참고)


데이터 베이스 값을 수정 한 후 Unique 로 바꿔주어 DB에서 중복저장을 못하게 막는다

이미지 클릭


DB 에서 막아준 뒤 JAVA 에서도 아이디 중복처리를 해주어야 한다

이미지 클릭

MemberController.java

 

[출처] https://m.blog.naver.com/PostView.nhn?blogId=allkanet72&logNo=220964699929&proxyReferer=https:%2F%2Fwww.google.com%2F

[참고]

[출처] https://bactoria.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-addAttribute-addFlashAttribute-%EC%B0%A8%EC%9D%B4%EC%A0%90-RedirectAttributes-rttr-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%ED%8A%B8

[요약]

메소드 redirect requestScope URL 새로고침 알터창
  model.addAttribute("message", "아이디 중복");   return "redirect:[경로]"; X X X X
  model.addAttribute("message", "아이디 중복");   return "[경로]"; O X O O
  flash.addAttribute("message", "아이디 중복");   return "redirect:[경로]"; X O O X
  flash.addAttribute("message", "아이디 중복");   return "[경로]"; X X X X
  flash.addFlashAttribute("message", "아이디 중복");   return "redirect:[경로]"; O (1회성) X X O
  flash.addFlashAttribute("message", "아이디 중복");   return "[경로]";
X X X X

 


이미지 클릭

MemberServiceImpl.java


MemberDao.java


memberMapper.xml


signUp.jsp

실행 화면 (이미지 클릭)

 


7. Vaild (유효성검사)

[필요 의존성]

(@Valid / @NotNull @ Empty / @Pattern)

1
2
3
4
5
6
7
<!-- Hibernate -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>5.4.3.Final</version>
</dependency>
 
cs

 

Hibernate 를 이용하여 유효성검사를 합니다

(Spring 버전에 맞는 Hibernate를 사용해야 실행오류가 발생하지 않습니다)

이미지 클릭

Member.java

 

[참고]

 

[Spring] 유효성 검사 ( form validation )

Validation validation이란 어떤 데이터의 값이 유효한지, 타당한지 확인하는 것을 의미합니다. 예를들어 이메일 주소 양식은 admin@example.com인데, 회원 가입을 할 때 이메일 양식이 일치하지 않으면 유

victorydntmd.tistory.com

 


정규식 검사하는 법

 

예시
  heydaze0327   사용가능
  h0327   사용가능
  heydaze   사용가능
  heydaze032712   12글자 이하
  @eydaze0327   특수문자 사용불가, 첫 문자는 영소문자
  3heydaze0327   첫 문자는 영소문자
  헤heydaze0327   첫 문자는 영소문자
  hey3   5글자 이상

 


 

이미지 클릭

MemberController.java


이미지 클릭

signUp.jsp

 

[참고] BindingResult


보강

이미지 클릭

 

실행화면 (이미지 클릭)

 

이 외에도 Spring <form:form> 태그를 활용한 error 출력 방법도 있습니다

 

#18. 로그

[필요 의존성]

(<org.springframework-version> 은 위에서 적었다면 안적어도 됩니다)

1
2
3
4
5
6
<!-- Spring Version, slf4j Version -->
<properties>
  <org.springframework-version>5.0.8.RELEASE</org.springframework-version>
  <org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
 
cs

 

Spring Context 에서 common-logging 파일을 제외한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Spring Context -->
<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>
 
cs

 

Slf4j 와 log4J 의존성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!-- 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>
 
cs

 

생성 위치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?mappers version="1.0" encoding="UTF-8"?>
 
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
 
<log4j:configuration>
 
    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %-5p: %c - %m%n"/>
        </layout>
    </appender>
 
    <!-- Application Loggers -->
    <logger name="Controller">
        <level value="info"/>
    </logger>
 
    <!-- 3rdparty Loggers -->
    <logger name="org.springframework.core">
        <level value="info"/>
    </logger>
 
    <logger name="org.springframework.beans">
        <level value="info"/>
    </logger>
 
    <logger name="org.springframework.context">
        <level value="info"/>
    </logger>
 
    <logger name="org.springframework.web">
        <level value="info"/>
    </logger>
 
    <!-- Root Logger -->
    <root>
        <priority value="info"/>
        <appender-ref ref="console"/>
    </root>
 
</log4j:configuration>
 
cs

 

기록을 남겨둠으로써 에러에 대한 처리를 빠르게 대처할 수 있다.

console 기록/ 메일로 기록/ 파일로 기록 등 다양하게 기록할 수 있다.

log4j2 까지 있으며, log4j 이외에도 logback 라이브러리로도 로그를 기록할 수 있다.

 

[참고]

 

[LOG] Log4j, LogBack 정리

Log4j 및 LogBack 정리. 1. Log4j(Log for Java) 특징 “Log4j” (현재는 Apache Logging Service라는 Top Project)는 “Ceki Gülcü“라는 개발자가 최초로 만들었다. 1.1 주요 기능 및 특징 ① 속도와 유연성..

goddaehee.tistory.com

 

1. request로 생성 된 member 객체 ToString()

이미지 클릭

MemberController.java

 

실행 화면 (이미지 클릭)


2. MemberMapper 에 대한 debug 로그

이미지 클릭

 

실행 화면 (이미지 클릭)

 


 

[출처] https://myblog.opendocs.co.kr/archives/950

<root> 출력 레벨이 INFO 이기 때문에 그 보다 레벨이 낮은 DEBUG 는 불러올 수 없다.

그래서 logger 를 추가하여 원하는 패키지 또는 클래스에 대해서만 자세한 DEBUG 정보를 출력할 수 있다.

 

name="member" 부분이 바로 클래스 또는 패키지 인데 출력이 가능한 이유는

mapper의 namespace가 패키지 또는 클래스명으로 설정해준다.

namespace="com.example.demo.mapper.member" 라고 설정 할 경우

해당 패키지(com.example.demo.mapper.member) 와 member 클래스는 존재하지 않지만

namespace 로 인하여 명시하게 된다.

 

member 처럼 패키지 없는 클래스만으로 적는 경우는 장기적으로 볼 때 좋지않다.

logger 같은 경우 패키지 별로 나뉘어서

어떤 곳에서는 ERROR 이상만 또 어떤 곳에서는 DEBUG 이상만 등

달리 설정해야 할 필요가 있기 때문이다

 

이미지 클릭

나중을 위해서 namespace 는 com.example.demo.mappers.memberMapper 로 수정하고,

DAO 클래스도 바꿔주어야 한다.

 

이미지 클릭

NAMESPACE 는 문자일 뿐이니

저는 편의상 .을 위로 올려 mapper sql의 id 부분만 적게끔 하였습니다.


3. Appender 날짜별 파일 기록로그

이미지 클릭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 날짜별 파일 Appender -->
<appender name="DAILY_ROLLING_FILE_APPENDER" class="org.apache.log4j.RollingFileAppender">
    <!-- 파일 덮어씌우기 -->
    <param name="append" value="true" />
    <!-- 파일 최대 용량 (KB/MB/GB) -->
    <param name="maxFileSize" value="10KB" />
    <!-- 일자별 백업파일의 보관기간 -->
    <param name="maxBackupIndex" value="5" />
    <!-- 파일 생성 위치 + 파일명 -->
    <param name="file" value="${catalina.home}/logs/log4j.log" />
    <!-- 기록 패턴 -->
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern"
               value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
    </layout>
</appender>
 
cs

 

경로 위치
  <param name="file" value="${catalina.home}/logs/log4j.log" />
  (톰캣 구동 시)
  <param name="file" value="${catalina.base}/log4j.log" />
  (톰캣 구동 시)
  <param name="file" value="./logs/log4j.log" />
  (톰캣 구동 시)
  <param name="file" value="logs/log4j.log" />
  (톰캣 구동 시)

  <param name="file" value="/logs/log4j.log" />
  (톰캣 구동 시)
  <param name="file" value="E:/logs/log4j.log" />

 


4. AOP 를 이용한 로그기록

[필요 의존성]

( 선택 1 또는 선택 2 둘 중 하나만 추가 / <org.springframework-version> 은 위에서 적었다면 안적어도 됩니다) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- 선택 1 -->
 
<properties>
  <org.aspectj-version>1.6.10</org.aspectj-version>
</properties>
 
<!-- AspectJ -->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>${org.aspectj-version}</version>
</dependency>
 
<!-- AspectJtools -->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjtools</artifactId>
  <version>${org.aspectj-version}</version>
</dependency>
 
<!-- 선택 2 -->
 
<properties>
  <org.springframework-version>5.0.8.RELEASE</org.springframework-version>
</properties>
 
<!-- spring Aspect -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>${org.springframework-version}</version>
</dependency>
 
cs

 

테스트 처리시간이 짧아서 Thread.sleep(2000); 을 사용하였습니다

(지워도 되는 코드)

 

이미지 클릭

Thread.sleep(2000); 을 제외하면 결과적으로 처리속도는

signUp GET은

0.02초

signUp POST 는

0.19초

 

메소드 정보 그림
Arrays.toString(pjp.getArgs()) 메소드를 호출 할 때 넘겨준 인자 목록
(매개변수)
pjp.getKind() 해당 Advice의 타입
pjp.getTarget()
pjp.getThis()
Target 객체를 알아낼 때
pjp.getSignature()
pjp.getSignature().toString()
실행하는 대상 객체의 메소드 정보
pjp.getSignature().toLongString() 실행하는 대상 객체의 메소드 정보
(자세히)
pjp.getSignature().toShortString() 실행하는 대상 객체의 메소드 정보
(간략히)
pjp.getSignature().getDeclaringTypeName() 실행 대상 객체의 패키지+ 클래스
pjp.getSignature().getDeclaringType() 실행 대상 객체의
클래스종류 + 패키지 + 클래스
pjp.getSignature().getName() 실행 대상 객체의 메소드명
new Modifier().toString(
pjp.getSignature().getModifiers()
)
실행 대상 객체의 접근자
(int 반환 때문에 Modifier 사용)

 


5. mybatis SQL 문 출력

[필요 의존성]

1
2
3
4
5
6
7
<!-- log4jdbc -->
<dependency>
  <groupId>org.bgee.log4jdbc-log4j2</groupId>
  <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
  <version>1.16</version>
</dependency>
 
cs

 

log4j.xml 내용 추가

1
2
3
4
5
6
7
8
9
<!-- Log4jdbc -->
<logger name="jdbc"><level value="OFF" /></logger>
<logger name="jdbc.sqlonly"><level value="OFF" /></logger> <!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
<logger name="jdbc.sqltiming"><level value="DEBUG" /></logger> <!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
<logger name="jdbc.audit"><level value="OFF" /></logger> <!-- ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남긴다. -->
<logger name="jdbc.resultset"><level value="OFF" /></logger> <!-- ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남긴다. -->
<logger name="jdbc.resultsettable"><level value="DEBUG" /></logger> <!-- SQL 결과 조회된 데이터의 table을 로그로 남긴다. -->
<logger name="jdbc.connection"><level value="OFF" /></logger> <!-- Connection 정보 로그를 남긴다 -->
 
cs

 

log4jdbc.log4j2.properties

생성 위치

1
2
3
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0
 
cs

 

이미지 클릭

1
2
3
4
5
6
7
8
9
<!-- log4jdbc Setting -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close">
    <property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
    <property name="url"
              value="jdbc:log4jdbc:mariadb://[서버아이피]:3306/test?autoReconnect=true&amp;useSSL=false" /> <!-- DataBase URL -->
    <property name="username" value="[DB 아이디]" /> <!-- DataBase ID -->
    <property name="password" value="[비밀번호]" />  <!-- DataBase Password -->
</bean>
 
cs

기존에 작성되어있던 bean을 주석처리하거나 삭제하거나 위 처럼 내용을 수정해주세요

 

destroy-method="close" 삭제

value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" 수정

log4jdbc 추가

 

위 처럼 수정한 경우 Tomcat 실행은 정상적으로 실행이 되나

웹 에서 DB로 접근 시 오류 발생합니다

 

쿼리문 출력을 위해 jUnit을 사용합니다.

모든 설정을 저 처럼한경우 4.13 이외에 버전은 오류가 발생합니다.

 

[필요 의존성]

(@RunWith / @Test / @Before / @After / @ContextConfiguration / Assert / MockMvc)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- Spring Test -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>${org.springframework-version}</version>
  <scope>test</scope>
</dependency>
 
<!-- Spring jUnit   -->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13</version>
  <scope>test</scope>
</dependency>
 
cs

 

이미지 클릭

 


다음 작업을 위해서 이전내용으로 바꿔줍니다

applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
 
    <!-- DBCP Setting -->
    <bean id="dataSource"
          class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName"
                  value="org.mariadb.jdbc.Driver" />                  <!-- DataBase Driver -->
        <property name="url"
                  value="jdbc:mariadb://[서버아이피]:3306/test?autoReconnect=true&amp;useSSL=false" /> <!-- DataBase URL -->
        <property name="username" value="[DB 아이디]" />                 <!-- DataBase ID -->
        <property name="password" value="[비밀번호]" />                <!-- DataBase Password -->
    </bean>
 
    <!-- log4jdbc Setting -->
<!--    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">-->
<!--        <property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />-->
<!--        <property name="url"-->
<!--                  value="jdbc:log4jdbc:mariadb://[서버아이피]/test?autoReconnect=true&amp;useSSL=false" /> &lt;!&ndash; DataBase URL &ndash;&gt;-->
<!--        <property name="username" value="[DB 아이디]" />                 &lt;!&ndash; DataBase ID &ndash;&gt;-->
<!--        <property name="password" value="[비밀번호]" />                &lt;!&ndash; DataBase Password &ndash;&gt;-->
<!--    </bean>-->
 
    <!-- SqlSessionFactory 객체 주입 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- mybatis-config.xml -->
        <property name="mapperLocations">
            <list>
                <value>classpath:mappers/**/*Mapper.xml</value>                <!-- *Mapper.xml -->
            </list>
        </property>
    </bean>
 
    <!-- SqlSession 객체 주입 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
 
    <!-- @Component Scan -->
    <context:component-scan base-package="com.example.demo.aop"/> <!-- Aop -->
    <context:component-scan base-package="com.example.demo.service"/> <!-- Service -->
    <context:component-scan base-package="com.example.demo.dao"/>     <!-- Repository -->
 
</beans>
 
cs

 

ContollerAdvice.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.example.demo.aop;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
 
@Component
@Aspect
public class ControllerAdvice {
 
    private static final Logger logger = LoggerFactory.getLogger(ControllerAdvice.class);
 
    @Around("execution(* com.example.demo.controller.MemberController.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
 
        logger.info("실행 전----");
 
        StopWatch sw = new StopWatch();
 
        sw.start(pjp.getKind());
 
        Object result = pjp.proceed();
 
        logger.info("실행 후----");
 
        sw.stop();
 
        double totalTime = sw.getTotalTimeMillis();
 
        logger.info("처리시간 {} 초"String.format("%.2f", totalTime/1000));
 
        return result;
 
    }
}
 
cs

 

MemberController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.example.demo.controller;
 
import com.example.demo.domain.Member;
import com.example.demo.service.MemberService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
 
@Controller
@RequestMapping("/member")
public class MemberController {
 
    private static final Logger logger = LoggerFactory.getLogger(MemberController.class);
 
    @Autowired
    private MemberService memberService;
 
    @PostMapping("/detail")
    public void detail(HttpSession session, Member resources) {
        Member member = new Member(resources.getAccount(), resources.getPassword());
        session.setAttribute("member", memberService.detail(member));
    }
 
    @GetMapping("/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "/home";
    }
 
    @GetMapping("/signUp")
    public void signUp(@ModelAttribute Member member) {
 
    }
 
    @PostMapping("/signUp")
    public String signUp(@Valid Member resources, BindingResult result, RedirectAttributes flash) {
        Member member = new Member(resources.getAccount(), resources.getPassword());
 
        if (result.hasErrors()) {
            flash.addFlashAttribute("errors", result.getFieldErrors());
            return "redirect:/member/signUp";
        }
 
        /** 1:= 생성 성공 -1:= 아이디 중복 -2:= 오류 */
        int rs = memberService.create(member);
 
        if (rs == -1) {
            flash.addFlashAttribute("message""중복된 아이디 입니다");
            return "redirect:/member/signUp";
        } else if (rs == -2) {
            flash.addFlashAttribute("message""오류가 발생하였습니다");
            return "redirect:/member/signUp";
        }
        return "redirect:/home";
 
    }
 
}
 
cs

 

log4j.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?mappers version="1.0" encoding="UTF-8"?>
 
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
 
<log4j:configuration>
 
    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %-5p: %c - %m%n"/>
        </layout>
    </appender>
 
    <!-- 날짜별 파일 Appender -->
    <appender name="DAILY_ROLLING_FILE_APPENDER" class="org.apache.log4j.RollingFileAppender">
        <!-- 파일 덮어씌우기 -->
        <param name="append" value="true" />
        <!-- 파일 최대 용량 (KB/MB/GB) -->
        <param name="maxFileSize" value="10KB" />
        <!-- 일자별 백업파일의 보관기간 -->
        <param name="maxBackupIndex" value="5" />
        <!-- 파일 생성 위치 + 파일명 -->
        <param name="file" value="${catalina.home}/logs/log4j.log" />
        <!-- 기록 패턴 -->
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
        </layout>
    </appender>
 
    <!-- Application Loggers -->
    <logger name="Controller">
        <level value="info"/>
    </logger>
 
    <!-- 3rdparty Loggers -->
    <logger name="org.springframework.core">
        <level value="info"/>
    </logger>
 
    <logger name="org.springframework.beans">
        <level value="info"/>
    </logger>
 
    <logger name="org.springframework.context">
        <level value="info"/>
    </logger>
 
    <logger name="org.springframework.web">
        <level value="info"/>
    </logger>
 
    <!-- Log4jdbc -->
    <logger name="jdbc"><level value="OFF" /></logger>
    <logger name="jdbc.sqlonly"><level value="OFF" /></logger> <!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
    <logger name="jdbc.sqltiming"><level value="DEBUG" /></logger> <!-- SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다. -->
    <logger name="jdbc.audit"><level value="OFF" /></logger> <!-- ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남긴다. -->
    <logger name="jdbc.resultset"><level value="OFF" /></logger> <!-- ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남긴다. -->
    <logger name="jdbc.resultsettable"><level value="DEBUG" /></logger> <!-- SQL 결과 조회된 데이터의 table을 로그로 남긴다. -->
    <logger name="jdbc.connection"><level value="OFF" /></logger> <!-- Connection 정보 로그를 남긴다 -->
 
    <!-- Root Logger -->
    <root>
        <priority value="info"/>
        <appender-ref ref="console"/>
    </root>
 
</log4j:configuration>
 
cs

 

#19. 오류 처리

1. throw new CustomException

2. Exception Handler

3. ControllerAdvice

4. Exception View.jsp

 

프로그램이든 웹이든 이미 작성된 코드로만 움직인다

사용자들은 개발자가 의도하는 대로 100% 사용할 수 없다.

그렇기 때문에 예외상황이 일어나는데

운영측면에서는 이러한 예외상황 / 오류 / 버그 등 관리해야 할 필요가 있다.

그러기 위해서는 개발자는 예외상황 / 오류에 대해서 집중적으로 모아서 관리해야 할 필요가 있다.

 

일단 기존에 앞 전에서 아이디 형식 또는 아이디중복에 대한 

임시방편처리를 했다.

프로젝트는 상황에 맞춰 점점 커질 수 있기 때문에

만약 100개가 넘는 오류가 저런식으로 작성되어있다면

Controller / Service / Domain 등 오류를 찾는데 시간을 허비하게 된다.

 

오류들을 묶어서 관리를 해야한다.

   
  존재함   Existed
  존재하지않음   NotFound
  중복   Duplication
  형식 불일치   Invalid
  액세스 거부 (권한 부족)   AccessDenied

 

1. DB 추가

2. Controller

3. Service

4. Dao

5. Mapper

6. Jsp

 

#20. 게시판

 

#21. 파일업로드

[필요 의존성]

1
2
3
4
5
6
7
<!-- File Upload -->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>
 
cs

 

#22. 메일

[필요 의존성]

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- Mail Support -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>${org.springframework-version}</version>
</dependency>
 
<!-- Mail Sender -->
<dependency>
  <groupId>javax.mail</groupId>
  <artifactId>mail</artifactId>
  <version>1.5.6</version>
</dependency>
cs

https://seonhyungjo.github.io/Spring-SMTP/

 

스프링 이메일 보내기(smtp)

각각의 smtp별로 보내는 방식

seonhyungjo.github.io

https://offbyone.tistory.com/167

 

스프링프레임웍 - 메일 발송하기

스프링프레임웍의 JavaMailSenderImpl을 이용해서 메일을 발송하는 방법에 대해 알아 보겠습니다. 메일을 발송하려면 메일을 발송해주는 메일 서버(SMTP Server)가 있어야 합니다. 메일 서버를 통해 메�

offbyone.tistory.com

 

#23. 트랜젝션

[필요 의존성]

1
2
3
4
5
6
7
<!-- Spring Transaction -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>${org.springframework-version}</version>
</dependency>
 
cs

 

#∞. 서버배포

war 빌드 - winSCP 로 tomcat/webapp 이동 - war 파일 붙여넣기 - ROOT.war 로 이름 변경

- http://서버아이피:8080/home 접속