728x90
300x250

[Spring-Framework] 31. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (2)


1부에서는 통상적인 개발 방법으로 간단한 Calculator 클래스에 정의된 sum 함수(또는 매서드)를 출력하는 방법을 살펴보았다.

참고로 앞에 코드는 편집이 불가능한 코드라고 가정하고 접근해야 한다.


1. [Spring-Framework] 31. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (1)

https://yyman.tistory.com/1447



8. aop 패키지를 추가로 작성하여 copy & paste하기


aop 패키지를 별도로 둘 것이다.

코드를 따로 작성해야 하는가? 시점 부분만 작성하고 크게 많이 작성하진 않는다.



그림 15. Copy & Paste 하기


태스트 코드를 복사할 것이다.


(1단계) com.local.example.service에 만들어놓았던 Calculator.java를 파일 복사한다. (Ctrl + C)

(2단계) com.local.example.aop 폴더(또는 패키지)를 만든다.

(3단계) com.local.example.aop 폴데 Calculator.java를 붙여넣는다. (Ctrl + V)

(4단계) 소스 코드가 빨간 불이 들어오는 패키지 부분을 com.local.example.aop로 변경해준다.



9. 작업 방법론 - 정리해보기


직관적으로 코드 작성 전략을 소개해주려고 한다.

이런 형태로 작업을 할 것이다.



그림 16. 작업 방법


빌드하는 방법은 웹 서버에 올려서 확인하면 된다.




9. AOP 필수(코어), 핵심사항 - RootConfig.java (환경설정 파일 만들기)


패키지 경로: com.local.example.aop


반드시 만들어야 할 파일이다.

이 셋팅 파일이 없으면 동작 자체를 안 한다.



그림 17. RootConfig.java


이 코드는 AspectJ Proxy의 핵심 환경설정 파일이다.

이걸 정의하지 않으면, 동작이 안 된다.


package com.local.example.aop;


import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.EnableAspectJAutoProxy;


@Configuration

@ComponentScan(basePackages = {"com.local.example.aop"})

//@ComponentScan(basePackages = {"com.local.example.beans", "com.local.example.advisor"})

@EnableAspectJAutoProxy

public class RootConfig {


}


파일명: RootConfig.java


[첨부(Attachments)]

RootConfig.zip




10. AOP 적용, 핵심사항 - Calculator.java (살짝 수정하기)


패키지 경로: com.local.example.aop


작업 파일 원본의 권한을 가진 사람에게 하나 요청해도 무방하다. (초기 생성자 부분)

여기에서는 그런 거 신경쓰지 않고, 작업하겠다.


package com.local.example.aop;


import org.springframework.stereotype.Service;


@Service("cal")

public class Calculator {


private long x;

private long y;

private long z;

public Calculator() {

this.x = 1;

this.y = 2;

this.z = 3;

}

public Calculator(long x, long y, long z) {

this.x = x;

this.y = y;

this.z = z;

}

// 작업 원본(A 프로그래머 작성함)

public long sum() {

long result = x + y + z;

System.out.println("비즈니스 로직");

// long d = result / 0; - 일부러 만든 코드(After Throws 보여줄려고)

return result;

}

}



파일명: Calculator.java


[첨부(Attachments)]

Calculator-Aspect.zip


(참고)

주석 친 부분을 주석 풀면, After Throws를 정의한 경우라면 메시지가 출력되는 것을 볼 수 있다.


색깔 칠한 코드는 Calculator(long x, long y, long z) 코드 때문에 Calculator.class 파일로 불러오면 Error를 경험할 수 있다.

그래서 파란색깔로 칠한 코드 Calculator()를 정의해서 문제를 해결한 것이다.




11. AOP 구현(시점 구현) - 시점 예제 (Log.java)


패키지 경로: com.local.example.aop


많은 고민을 한 끝에 간단하면서 실용적인 형태로 코드를 작성하였다.

참고로 시점 정의를 안해도 빌드해보면, HomeController.java에 정의가 잘 되어있으면 돌아가긴 돌아간다.

무슨 말인지는 태스트를 해보면 알 수 있다.


package com.local.example.aop;


import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.AfterThrowing;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Service;


// 관점, 서비스

@Aspect

@Service


// 콤포넌트 사용하지 말것

public class Log {

Logger logger =  LoggerFactory.getLogger(Log.class);

// 섞어적어놓아도 순서를 찾아서 강제 인식함.

    

// 2단계 - 전 단계 시야)

@Before("execution(* com.local.example.aop.Calculator.*(..))")

// @Before("execution(public void sum())")

// 반환값 없어도 무방

    public void logBefore() { 

logger.info("전 단계 관점 - 구현(Before)");

    }

    

// 1단계 - Before보다 전 단계 실행

    // Calculator 클래스의 모든 메서드

// 무조건 반환값이 지정되어야 함. (ProceedingJoinPoint에 대한 Object 반환값 필수 정의되어야 함)

// @Around 이 친구만 Pjp가 있어야 함.

    @Around("execution(* com.local.example.aop.Calculator.*(..))")

    /*@Around("execution(* com.example.demo.controller..*.*(..))")*/

    /*@Around("execution(* com.example.demo..*.*(..))")*/

    public Object logAround(ProceedingJoinPoint pjp) throws Throwable { 

   

logger.info("Around 단계 - 구현(Around)");

Object result = pjp.proceed();

return result;

    }

    

    // 3단계는 (중간) - 임의로 지정해줄 수 있는 것이 아님.

// 4단계: After advice

//@After("execution(* method1())")  -- method1 함수만 실행 

    // (이렇게 하면 안 됨. sum()을 콜하니깐)

    // @After("execution(* method1())")

    @After("execution(* sum())")

public void afterMethod() {

   

    logger.info( "after Method 호출" );

   

}

    

    // 5단계: 맨 마지막 단계

// after-throwing advice /// 오류가 발생할 때 호출함(무조건 보여지는 영역은 아님

    // 쉽게 비유하면, try to catch finally에서 catch 단계로 보면 됨.

    // 오류도 출력되고 after Throwing 로그 메시지도 출력된다.

    // 이 화면을 보려면, 오류나는 코드에 try to catch finally를 적용하면 안 됨.

    // 원본 비즈니스 로직 코드는 그대로 둘 것

    @AfterThrowing(

        pointcut = "execution(* com.local.example.aop.Calculator.sum(..))", 

        throwing = "ex"

    )

public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {

logger.info( "after Throwing 호출" + ex.getMessage());

}


    

}


파일명: Log.java


[첨부(Attachments)]

Log.zip





12. AOP 사용시, 핵심사항 - HomeController.java 변경하기


패키지 경로: com.local.example


HomeController.java 코드를 수정할 것이다.


package com.local.example;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import org.springframework.context.annotation.EnableAspectJAutoProxy;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.local.example.aop.Calculator;

import com.local.example.aop.RootConfig;


/**

 * Handles requests for the application home page.

 */

@EnableAspectJAutoProxy

@Controller

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

/**

* Simply selects the home view to render by returning its name.

*/


@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

// Java 방식 - AOP

AnnotationConfigApplicationContext context = 

new AnnotationConfigApplicationContext(RootConfig.class);

// 1. 이전 방식

// Calculator cal = new Calculator(1, 2, 3);


// 2. Aspect 적용

// 소문자로 입력(Calculator -> calculator로)

Calculator cal = (Calculator)context.getBean("cal", Calculator.class);

System.out.printf("result of sum: %d", cal.sum());

model.addAttribute("serverTime", formattedDate );

return "home";

}

}



파일명: HomeController.java


[첨부(Attachments)]

HomeController-aspect.zip




13. 동작 결과 구경하기


두 가지 결과로 작성하였다. 하나는 오류가 없는 코드이다.

하나는 오류가 있어서 After Throws가 호출되는 코드이다.



그림 17. 결과1의 오류 없는 코드(1)



그림 18. 결과1의 오류 없는 코드(2)




그림 19. 결과2의 오류 있는 코드(1)



그림 20. 결과2의 오류 있는 코드(2)





* 맺음글(Conclusion)


AOP 프로그래밍을 실질적으로 쉽게 사용하는 방법에 대해서 소개하였다.



* 참고자료(References)


1. [스프링] Error creating bean with name '***Controller': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named '***Service' is defined 에러 해결방법, https://zzznara2.tistory.com/275, Accessed by 2020-10-04, Last Modified 2015-06-18.


참고: 시중에 있는 AOP 자료를 보고 따라하다보면, 오류 먼저 구경할 것이다. AOP는 조금 어느 정도 프로그래밍 지식이 있을 때 사용하는 걸 권장하고 싶다. (쉽지 않은 부분이다.)


2. Spring : AOP with Java Config - 설정하기와 @Before advice, https://kogle.tistory.com/51, Accessed by 2020-10-04, Last Modified 2020-05-11.


추천(40점): 구축 원리에 대해서 매우 잘 나와있다. 그러나 몇 가지는 수정작업을 해줘야 한다.


3. AspectJ Weaver를 사용한 애노테이션 기반의 스프링 AOP 구현 방법, https://atoz-develop.tistory.com/entry/AspectJ-Weaver%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%95%A0%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B0%98%EC%9D%98-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95, Accessed by 2020-10-04, Last Modified 2020-04-27.


추천(25점): 구축 원리를 잘 정리해놨다. 2번 참고자료하고 같이 보는 것을 권장한다. (예제가 살짝 아쉬움)


4. Spring AOP 스프링이 해줄건데 너가 왜 어려워 해? Spring boot에서 aop logging 사용법 제일 쉽게 알려드립니다!, https://jeong-pro.tistory.com/171, Accessed by 2020-10-04, Last Modified 2018-11-23.

반응형
728x90
300x250

[Spring-Framework] 30. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (1)


참고로 AOP는 Spring-Framework에만 적용되는 방법론이다.

새로 만들어진 개념은 아니고, 자바의 Proxy(패턴 아님. 탈취기법이 있음.)에서 핵심 영역의 관점으로 진화한 개념이다.


교과서나 시중 책 등에서는 이런 표현은 안 쓰긴 하지만, "탈취 방법"이라고 하는 게 적절하다고 주장하다.


해당 작업은 Spring Framework에서만 가능한 작업이다.



IDE: Spring Tool-Suite 4-4.7.2 Release(2020-08월)
Library: 

- Spring Framework 5.2.9.RELEASE (2020-09-15)

- Java Version: 14

- AspectJ: 1.9.6 (2020-07-22)

- AspectJ Weaver: 1.9.6 (2020-07-22)

- javax.servlet-api 4.0.1 (2018-04-20)


(순정 상태로 셋팅하였음.)



Project: Spring Legacy Project로 생성할 것


-> 안 보이는 사람들은 Help->Eclipse Marketplace -> STS 검색 후 "Spring Tools 3 Add-On for Spring Tools 4 3.9.14.RELEASE" 설치할 것



그림 1. STS-Addon (Eclipse Marketplace)



1. 이전의 개발 방법론(Proxy)


지금도 물론 이런 방법은 안 쓰일 수는 없다.

AOP가 먼저 나온 것이 결코 아니다. 아마 c#에도 이런 게 있는 걸로 알고 있다. (오래되서)


프로그래밍 개발을 하다보면, 로직을 구현하는데 있어서 코어 코드를 수정해서 작업을 하는 일이 많이 있다.

핵심 규칙은 x, y, z만 더해서 결과만 출력하면 되는 요구사항을 가진 문제인데 구현을 하다보면, 현실적인 문제라는 게 발생하게 된다.


그림 2. 이전의 로직 구현 방법 - 개발


그림 1과 같은 문제는 자주 생기는 문제이기도 하다.

이런 방법으로 구현하는 것이 아예 사라졌다는 게 아니다.


그림 1에서 (1단계)의 핵심 규칙을 재사용할 기회를 상실해버리는 문제가 발생하게 된다.



그림 3. 문제 인식


그림 2처럼 문제가 생겼다고 하자.

물론 머리가 조금 좋은 분들은 객체의 상속 관계로 처리해도 해결할 수 있지 않겠냐고 할 수도 있다. 

(객체지향 프로그래밍에서도 설계를 다시 해서 이런 문제를 나눠볼 수도 있겠음.)


허나 쉬운 일이 결코 아니다. 코드라는 게 이쪽에서 사용되고 있는지, 저기에서 사용되고 있는지 작성을 하게 되면 수정이 정말 어렵다는 것이다.


물론 C#을 논하는 자리가 아니어서 자바를 예로 들면, Proxy 탈취 방법이 존재한다.




그림 4. Proxy 탈취 방법 - 자바


자바에서는 


Type of Classes username new Proxy.newProxyInstance(Classes.class.getClassLoader(), new Class[] { classes.class } , 

new InvocationHandler(){

          public Object invoke(Object proxy, Method method, Object args[] throws Throwable{

                     

                  // 전단계 호출

                  Object result = method.invoke(sum, args);

                  // 후단계 호출

         }


     }


};


슈도 코드(Pseudo Code) 1. 자바의 Proxy 호출 방법


이런 방법으로 핵심 로직을 탈취하는 방법이 있다.

기본적인 원리는 이런 원리에 입각해서 만들어졌다고 보면 된다.


AOP의 용어 설명은 나중에 할 것이다. 절대로 지금 바로 알면 안 된다고 생각한다.


3단계를 기본으로 한다. "전 단계, 핵심 로직, 후 단계"

이 단계의 확장적인 개념이다.


Spring AOP에서는 5단계로 확장되었다.


전단계:    {Around 단계, Before 단계}, 

핵심로직:         핵심 로직(중간)

후단계:    {after Method 호출, AfterThrowing(선택 - 예외 처리 발생 때만)}


조금 더 이해하기 쉬워졌을 것으로 보인다.


[첨부(Attachments)]

background-aop.zip





2. 코드로 살펴보는 AOP


AOP를 이론으로만 봐서는 이해가 무척 안 될 것이다. 구현을 통해서 단계적으로 살펴보도록 하겠다.

프로젝트 생성부터 단계적으로 소개하겠다.



그림 5. 프로젝트 생성하기


File을 클릭한다.

New -> Spring Legacy Project를 클릭한다.



그림 6. 프로젝트 생성하기


Spring MVC Project를 선택한다.

Project name을 입력한다.

Next를 누른다.




그림 7. 프로젝트 생성하기


Top-level-package를 입력한다. (예: com.local.example)

Finish를 누른다.



3. POM.xml 설정하기


http://mvnrepository.com 사이트에 접속한다.

검색 키워드에 "aspectj"라고 입력한다. (세 가지 검색을 해서 버전을 획득해야 함)

검색 키워드에 "spring"라고 입력한다.

검색 키워드에 "servlet"이라고 입력한다.


두 가지의 Maven 주소를 획득해야 한다.



그림 8. MVNRepository 검색 결과의 예 (1) - AspectJ



그림 9. MVNRepository 검색 결과의 예 (1) - AspectJ


버전을 클릭하면, Maven Pom이 나온다.

이걸 복사 붙여넣기를 해도 된다.


다만 <version>만 복사해서 관리하는 방법도 있으니 자세한 건 POM.xml 소스를 참고하면 좋겠다.



그림 10. pom.xml - 변경 작업 모습(1)


<java-version>14</java-version>으로 변경한다.

<org.springframework-version>5.2.9.RELEASE</org........>로 변경한다.

<org.aspectj-version>1.9.6</org......>으로 변경한다.


<?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 https://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.local</groupId>

<artifactId>example</artifactId>

<name>springAOP-javaConfig</name>

<packaging>war</packaging>

<version>1.0.0-BUILD-SNAPSHOT</version>

<properties>

<!-- 자바 버전 변경함 -->

<java-version>14</java-version>

<!-- 스프링프레임워크 버전 변경함 -->

<org.springframework-version>5.2.9.RELEASE</org.springframework-version>

<!-- AspectJ 변경함 -->

<org.aspectj-version>1.9.6</org.aspectj-version>

<org.slf4j-version>1.6.6</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>

<!-- AspectJWeaver 추가 -->

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</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 -->

            <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> 

            <dependency> 

                  <groupId>javax.servlet</groupId>

                  <artifactId>javax.servlet-api</artifactId>

                  <version>4.0.1</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>



파일명: pom.xml


[첨부(Attachments)]

pom.zip





4. Project의 Properties - Build Path, Project Factes 설정하기 (Java Version 변경)


프로젝트의 자바 버전을 변경할 것이다.



그림 11. 프로젝트의 마우스 오른쪽 버튼 메뉴 모습


프로젝트를 선택한 후 마우스 오른쪽 버튼을 누른다.

Properties를 클릭한다.




그림 12. Java Build Path


Java Build Path를 클릭한다.

JRE System Library를 [JavaSE-14]로 변경한다.




그림 13. Project Factes


Project Factes를 클릭한다.

Java의 버전을 14로 바꿔준다.




5. web.xml - 서블릿 스팩 (2.5에서 4.0으로 변경)


서블릿 스팩을 변경할 것이다.

web.xml에서 변경해주면 된다.



그림 14. web.xml (servlet 4.0 스팩으로 변경)



<?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">


<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring/root-context.xml</param-value>

</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<!-- Processes application requests -->

<servlet>

<servlet-name>appServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>appServlet</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


</web-app>



파일명: web.xml


[첨부(Attachments)]

web.zip



6. service - Calculator.java


복잡한 설명을 적기보다는 작업화면하고 코드를 소개하겠다.



그림 15. Calculator.java


package com.local.example.service;


public class Calculator {


private long x;

private long y;

private long z;

public Calculator(long x, long y, long z) {

this.x = x;

this.y = y;

this.z = z;

}

// 작업 원본(A 프로그래머 작성함)

public long sum() {

long result = x + y + z;

return result;

}


}


파일명: Calculator.java


[첨부(Attachments)]

 Calculator-original.zip


참고로 지금 코드로는 AspectJ가 동작되지 않는 코드이다.
이유는 초기 매개변수가 없는 생성자가 없어서 그렇다.

(다음 페이지에서 소개하겠음.)



7. Controller - HomeController.java


아마 순정 코드에 Calculator의 sum()를 호출한 것을 보면, 다음과 같이 되어있다.


package com.local.example;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import org.springframework.context.annotation.EnableAspectJAutoProxy;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.local.example.aop.Calculator;

import com.local.example.aop.RootConfig;


/**

 * Handles requests for the application home page.

 */

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

/**

* Simply selects the home view to render by returning its name.

*/


@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);


Calculator cal = new Calculator(1, 2, 3);

System.out.printf("result of sum: %d", cal.sum());

model.addAttribute("serverTime", formattedDate );

return "home";

}

}



파일명: HomeController.java


이렇게 하면, 잘 동작할 것이다.

결과가 "result of sum: 6"이 출력될 것이다.



* 2부에서 AOP 사용 코드로 전환하는 방법에 대해서 소개하겠다.


1. [Spring-Framework] 30. AOP(Aspect-Oriented-Programming) 관점지향 프로그래밍 (Java셋팅) (2), 2020-10-04

https://yyman.tistory.com/1448


반응형
728x90
300x250

[JSP] 24. jQuery와 Ajax 멀티 파일 업로드(개선), POST전송, JSON - 구성하기(1)


이번에 게시할 프로젝트는 jQuery, Ajax, 파일 업로드, POST전송, JSON 구성하기라는 거창한 제목이긴 하지만, 실제로는 기능별로 분리하여 작업한 결과이다.

이 글은 기존에 게시글에서 작성한 업로드 기능에 "토큰 인증 방식", "파라메터 문자열 분석 및 분리" 등의 기능을 추가하였다.

아무래도 업로드 기능이니깐 그냥 업로드 하는 것보다는 토큰 하나 넣어주는 게 좋지 않겠냐는 생각이다.


1. [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1), 2020-09-24

https://yyman.tistory.com/1414

2. [JSP] 13. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2), 2020-09-24
https://yyman.tistory.com/1415

3. [JSP] 21. Jsp/Servlet(MVC) Maven 기반의 다중 업로드, 다운로드, 삭제 구현(1), 2020-10-01

https://yyman.tistory.com/1436




그림 1. COS(Servlet)


이거는 오래되서 미지원이다. (일부 인터넷 글에 따르면, 사용중이더라 이런 글들이 있는데 태스트를 해본 바로는 오류가 발생하고 안 된다.

(구체적으로 servlet 패키지에서 충돌남)


* IDE: Spring Tool-Suite 4.4 4-4.7.2. RELEASE (Eclipse)

* MVN(pom.xml)

   - javax.servlet-api 4.0.1 (https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api)

   - json - 20200518 (https://mvnrepository.com/artifact/org.json/json)

   - commons-io - 2.8.0 (https://mvnrepository.com/artifact/commons-io/commons-io)

   - commons-fileupload - 1.4 (https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload)

* Javascript: jquery-3.5.1.js


DB 자체를 제거했으며, 이해를 돕기 위해서 매우 순수한 구조로 작성되었다.


자료구조에서 LinkedList에 해당하는 자바의 ArrayList와 Map를 파일 업로드 부분에 적용하였음.



1. 결과


이번에 만들 작업에 관한 결과이다. 결과를 보고 무엇을 만들지 생각하는 방법을 터득했으면 좋겠다.



그림 1. 작업 결과



그림 2. 작업 결과 - 출력된 HTML 소스(1)



그림 3. 작업 결과 - 출력된 HTML 소스(2)



그림 4. 작업 결과(2) - ListJSONController.java



그림 5. 작업결과(3) - 파일 업로드 기능(1)



그림 6. 작업결과(4) - 파일 업로드 기능(2)



그림 6-1. JSON - 결과(1)



그림 6-2. JSON - 결과(2)



그림 6-3. JSON - 결과(3)



그림 6-4. JSON - 결과(4)




2. 프로젝트 구성도


만들어야 할 프로그램들이다. 조금 양이 많다.



그림 7. 프로젝트 구성




3. 프로젝트 생성


프로젝트 생성하는 방법에 대해서 소개하도록 하겠다.



그림 8. 프로젝트 생성하기(1)


File을 누른다.

New-> Maven Project를 클릭한다.

(1단계 중략 - Next만 눌러도 되는 부분)



그림 9. 프로젝트 생성하기(2)


webapp를 검색한다.

org.apache.maven.archetypes    |  maven-archetype-webapp  1.4을 선택한다.

Next를 누른다.



그림 10. 프로젝트 생성하기(3)


Group Id와 Artifact Id를 입력한다.

Finish를 누른다.



4. 프로젝트 속성 - Build Path, Project Factes 설정하기


자바 버전을 변경할 것이다.



그림 11. 프로젝트 선택 후의 마우스 오른쪽 버튼 메뉴


프로젝트를 선택한 후, 마우스 오른쪽 버튼으로 클릭한다.

Properties를 클릭한다.



그림 12. Java Build Path


Java Build Path를 클릭한다.

JRE System Library 버전을 14로 바꿔준다. [Edit 클릭 -> JRE -> Java SE -14]




그림 13. Project Factes


Project Factes를 클릭한다.

Java의 버전을 14로 바꿔준다.



5. pom.xml 수정하기


pom.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/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>


  <groupId>com.webedu</groupId>

  <artifactId>exampleAjax</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>war</packaging>


  <name>exampleAjax Maven Webapp</name>

  <!-- FIXME change it to the project's website -->

  <url>http://www.example.com</url>


  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <maven.compiler.source>1.7</maven.compiler.source>

    <maven.compiler.target>1.7</maven.compiler.target>

  </properties>


  <dependencies>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.11</version>

      <scope>test</scope>

    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>

<!-- https://mvnrepository.com/artifact/org.json/json -->

<dependency>

    <groupId>org.json</groupId>

    <artifactId>json</artifactId>

    <version>20200518</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->

<dependency>

    <groupId>commons-io</groupId>

    <artifactId>commons-io</artifactId>

    <version>2.8.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->

<dependency>

    <groupId>commons-fileupload</groupId>

    <artifactId>commons-fileupload</artifactId>

    <version>1.4</version>

</dependency>

  </dependencies>


  <build>

    <finalName>exampleAjax</finalName>

    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->

      <plugins>

        <plugin>

          <artifactId>maven-clean-plugin</artifactId>

          <version>3.1.0</version>

        </plugin>

        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->

        <plugin>

          <artifactId>maven-resources-plugin</artifactId>

          <version>3.0.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-compiler-plugin</artifactId>

          <version>3.8.0</version>

        </plugin>

        <plugin>

          <artifactId>maven-surefire-plugin</artifactId>

          <version>2.22.1</version>

        </plugin>

        <plugin>

          <artifactId>maven-war-plugin</artifactId>

          <version>3.2.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-install-plugin</artifactId>

          <version>2.5.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-deploy-plugin</artifactId>

          <version>2.8.2</version>

        </plugin>

      </plugins>

    </pluginManagement>

  </build>

</project>



파일명: pom.xml


[첨부(Attachments)]

pom.zip



6. Servlet 생성하기


서블렛 생성에 대한 것이다.



그림 14. Java Resources 폴더의 마우스 오른쪽 버튼 모습


Java Resources를 마우스 오른쪽 버튼으로 클릭한다.

New->Servlet을 클릭한다.



그림 15. Create Servlet


Java package를 입력해준다. (예: com.example.web.controller)

Class Name을 입력해준다. (예: FrontController)


Finish를 누른다.




7. Web.xml 수정하기


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


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

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

xmlns="http://java.sun.com/xml/ns/javaee" 

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 

id="WebApp_ID" version="3.0">

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

  <welcome-file-list>

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

  </welcome-file-list>

  <servlet>

  <servlet-name>FrontController</servlet-name>

  <servlet-class>com.example.web.controller.FrontController</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet-mapping>

  <servlet-name>FrontController</servlet-name>

  <url-pattern>*.do</url-pattern>

  </servlet-mapping>

</web-app>


파일명: web.xml


[첨부(Attachments)]

web.zip



8. Controller - Interface 생성하기


경로: /src/main/java/com/example/web/controller/Controller.java


package com.example.web.controller;


import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public interface Controller {


public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;


}




파일명: Controller.java


[첨부(Attachments)]

Controller.zip



9. Controller - board/InsertController.java


경로: /src/main/java/com/example/web/controller/board/InsertController.java


package com.example.web.controller.board;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import com.example.web.controller.Controller;

import com.example.web.util.HttpUtil;


public class InsertController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

HttpUtil.forward(req, res, "/WEB-INF/views/board/result.jsp");


}


}


파일명: InsertController.java


[첨부(Attachments)]

InsertController.zip




10. Util - util/HttpUtil.java (업로드 개선 및 문자열 구분 처리)


토큰, 업로드 개선, 문자열 구분 처리 등을 추가적으로 담아보았다.


경로: /src/main/java/com/example/web/util/HttpUtil.java


package com.example.web.util;


import java.io.File;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;


import javax.servlet.RequestDispatcher;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class HttpUtil extends HttpServlet {


private static final long serialVersionUID = 1L;


private static String charset = null; // 문자열


    private static final int MEMORY_THRESHOLD   = 1024 * 1024 * 3;  // 3MB // 

    private static final long MAX_FILE_SIZE      = 1024 * 1024 * 40; // 40MB

    private static final long MAX_REQUEST_SIZE   = 1024 * 1024 * 50; // 50MB

 

private static String UPLOAD_FOLDER = "upload";

private static String UPLOAD_TMP_FOLDER = File.separator + "WEB-INF" + File.separator + "temp";

private static List<String> reqInfoList = null; // req 정보(MultiRequest) - 문맥 방식

private static List<Object> fileInfoList = null; // 다중 파일 지원 - 문맥 방식

private static Map<String, Object> reqInfoMap = null; // req 정보(MultiRequest) - Map 자료구조

private static Map<Integer, Map<String, Object>> fileInfoMap = null; // 다중 파일 지원 - Map 자료구조

private static boolean FILE_TOKEN_CHECK = false; // req 토큰 체크하기 

private static String FILE_AUTH_TOKEN_KEY = null; // req 인증키 추가

private static String FILE_RESTRICT_EXT = ".jpg.jpeg.bmp.png.gif.txt"; // 파일 확장자 제한

private static int reqNum = 0; // reqNum

private static int fileNum = 0; // fileNum


public static void forward(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {


try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);


}catch(Exception e) {

e.printStackTrace();

}

}

public static void uploadFile(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {


reqInfoList = new ArrayList<String>();

fileInfoList = new ArrayList<Object>();


fileInfoMap = new HashMap<Integer, Map<String, Object>>(); 

reqInfoMap = new HashMap<>();

// 파일 인증키 인증 완료 후 업로드

List<FileItem> tmpItems = new ArrayList<FileItem>();

FILE_AUTH_TOKEN_KEY = "1";

reqNum = 1;

fileNum = 1;

    

    PrintWriter out = res.getWriter();

    //out.println("<HTML><HEAD><TITLE>Multipart Test</TITLE></HEAD><BODY>");

try {

        

        //디스크상의 프로젝트 실제 경로얻기

        //String contextRootPath = "c:" + File.separator + "upload";

String dirName = UPLOAD_FOLDER ; 

// String dirName = "upload"; 

String contextRootPath = req.getSession().getServletContext().getRealPath("/") + dirName;

        System.out.println("실제경로:" + contextRootPath);

        

        //1. 메모리나 파일로 업로드 파일 보관하는 FileItem의 Factory 설정

        DiskFileItemFactory diskFactory = new DiskFileItemFactory(); // 디스크 파일 아이템 공장

        diskFactory.setSizeThreshold(MEMORY_THRESHOLD); // 업로드시 사용할 임시 메모리

        // diskFactory.setSizeThreshold(4096); // 업로드시 사용할 임시 메모리

        diskFactory.setRepository(new File(contextRootPath + UPLOAD_TMP_FOLDER)); // 임시저장폴더

        // diskFactory.setRepository(new File(contextRootPath + UPLOAD_TMP_FOLDER)); // 임시저장폴더

        

        //2. 업로드 요청을 처리하는 ServletFileUpload생성

        ServletFileUpload upload = new ServletFileUpload(diskFactory);

        // upload.setSizeMax(3 * 1024 * 1024); //3MB : 전체 최대 업로드 파일 크기

        upload.setSizeMax(MAX_REQUEST_SIZE);

        

        // sets maximum size of upload file(파일 단위 업로드 크기)

        upload.setFileSizeMax(MAX_FILE_SIZE);

        

        //3. 업로드 요청파싱해서 FileItem 목록구함​​

        List<FileItem> items = upload.parseRequest(req); 

        Iterator<FileItem> iter = items.iterator(); //반복자(Iterator)로 받기​            

        while(iter.hasNext()) { //반목문으로 처리​    

            FileItem item = (FileItem) iter.next(); //아이템 얻기

             //4. FileItem이 폼 입력 항목인지 여부에 따라 알맞은 처리

            

            if(item.isFormField()){ 

            //파일이 아닌경우

                processFormField(out, item);

                

            } else {

            //파일인 경우

            // System.out.println("오류:" + item.getName());

           

            // 버그 개선 item 이름값 비어있을 때

            if ( item.getName() != "") {

            tmpItems.add(item);

            // processUploadFile(out, item, contextRootPath);

            }

            // System.out.println("오류2:");

            }

        }

        

        // 파일 토큰 확인될 경우

        if ( FILE_TOKEN_CHECK ) {

       

        // 파일 업로드 처리

        for (FileItem readyItem: tmpItems)

        {

        processUploadFile(out, readyItem, contextRootPath);

        }

       

        }

        

        

        

    } catch(Exception e) {

        e.printStackTrace(out);

    }

//out.println( "usrID(Map): " + reqMap.get("usrID") );

//out.println( "usrPasswd(Map):" + reqMap.get("usrPasswd") );

    

    // out.println("</BODY></HTML>");

// req.setAttribute("usrID", reqMap.get("usrID"));

// req.setAttribute("login", 1);//Object Type으로 넘어감

    req.setAttribute("reqInfoMap", reqInfoMap);

    req.setAttribute("fileInfoMap", fileInfoMap);

    req.setAttribute("reqInfoList", reqInfoList);

    req.setAttribute("fileInfoList", fileInfoList);

    

// System.out.println("오류3:" + reqMap.get("usrID"));

    

}

//업로드한 정보가 파일인경우 처리

private static void processUploadFile(PrintWriter out, FileItem item, String contextRootPath)

throws Exception {

Map<String, Object> fileMapNode = new HashMap<String, Object>();

List<String> fileListNode = new ArrayList<String>();

boolean resultUpload = false;

String dirName = UPLOAD_FOLDER ; 

String name = item.getFieldName(); // 파일의 필드 이름 얻기

String fileName = item.getName(); // 파일명 얻기

// 임시 - 실제 원본 이름 추출

File originalFile = new File(fileName);

String originalFileName = originalFile.getName();

System.out.println("임시:" + originalFileName );

String contentType = item.getContentType(); // 컨텐츠 타입 얻기

long fileSize = item.getSize(); // 파일의 크기 얻기

// 업로드 파일명을 현재시간으로 변경후 저장

String fileExt = fileName.substring(fileName.lastIndexOf("."));

String uploadedFileName = System.currentTimeMillis() + ""; 

System.out.println(fileExt);

System.out.println(uploadedFileName);

// 저장할 절대 경로로 파일 객체 생성

String realUploadFile = File.separator + dirName + File.separator + uploadedFileName;

System.out.println("실제 저장직전폴더:" + contextRootPath + realUploadFile);

File uploadedFile = new File(contextRootPath + realUploadFile);

if ( FILE_RESTRICT_EXT.contains(fileExt.toLowerCase()) ) {

item.write(uploadedFile); // 파일 저장

resultUpload = true; // 파일 업로드 완료

}

//========== 뷰단에 출력 =========//

//out.println("<P>");

//out.println("파라미터 이름:" + name + "<BR>");

//out.println("파일 이름:" + fileName + "<BR>");

//out.println("콘텐츠 타입:" + contentType + "<BR>");

//out.println("파일 사이즈:" + fileSize + "<BR>");

//확장자가 이미지인겨우 이미지 출력

/*

if(".jpg.jpeg.bmp.png.gif".contains(fileExt.toLowerCase())) {

out.println("<IMG SRC='upload/" 

+ uploadedFileName 

+ "' width='300'><BR>");

}

*/

// out.println("</P>");

// out.println("<HR>");

// out.println("실제저장경로 : "+uploadedFile.getPath()+"<BR>");

// out.println("<HR>");

// 파일 전송이 완료되었을 때

if ( resultUpload == true ) {

// 파일 정보(Map 자료구조 방식)

fileMapNode.put("name", name);

fileMapNode.put("fileName", originalFileName);

fileMapNode.put("contentType", contentType);

fileMapNode.put("fileSize", fileSize);

fileMapNode.put("fileExt", fileExt);

fileMapNode.put("uploadedFileName", uploadedFileName);

fileMapNode.put("realName", uploadedFile.getName());

fileMapNode.put("realPath", uploadedFile.getPath());

// 파일 정보(문자열 구분 방식)

fileListNode.add( "id:" + fileNum + ",name:name,value:" + name + "/" );

fileListNode.add( "id:" + fileNum + ",name:filename,value:" + originalFileName + "/" );

fileListNode.add( "id:" + fileNum + ",name:contentType,value:" + contentType + "/" );

fileListNode.add( "id:" + fileNum + ",name:fileSize,value:" + fileSize + "/" );

fileListNode.add( "id:" + fileNum + ",name:fileExt,value:" + fileExt + "/" );

fileListNode.add( "id:" + fileNum + ",name:uploadedFileName,value:" + uploadedFileName + "/" );

fileListNode.add( "id:" + fileNum + ",name:realName,value:" + uploadedFile.getName() + "/" );

fileListNode.add( "id:" + fileNum + ",name:realPath,value:" + uploadedFile.getPath() + "/" );

fileInfoList.add(fileListNode);

fileNum++;

}

}

private static void processFormField(PrintWriter out, FileItem item) 

throws Exception{

String name = item.getFieldName(); //필드명 얻기

Object value = item.getString("UTF-8"); //UTF-8형식으로 필드에 대한 값읽기

//out.println(name + ":" + value + "<BR>"); //출력

// 파일 토큰 인증 여부

if ( name.contentEquals("token")

&& value.equals(FILE_AUTH_TOKEN_KEY) ) {

System.out.println("참 - 인증");

FILE_TOKEN_CHECK = true;

}

// Map 자료구조 저장

reqInfoMap.put(name, value);

// 문자열 구문 저장 - 문자열 검색원리 적용 (검색 기법 적용)

reqInfoList.add( "id:" + reqNum + ",name:" + name + ",value:" + value + "/" );

reqNum++;

}

// FileInfoList 내용 - 문맥 문석기(임시)

public static void getFileinfoParser(List<Object> fileParser, String tarName) {

// 분석 시작

if ( fileParser != null ) {

for ( Object fileNode : fileParser ) {

@SuppressWarnings("unchecked")

List<String> sep = (List<String>) fileNode;

// 문자열 분석 반환 받기 (id = 0, name = 1, value = 2)

Object result = getFileAttrAnal(sep, tarName);

}

}

}


// FileInfoList 내용 - 문자열 구분

private static Object getFileAttrAnal(List<String> fileNode, String tarName) {

int pId = -1;

String pName = null;

Object pValue = null;

String trashTxt = null;

if ( fileNode != null) {

for ( String usrTxt : fileNode  ) {

trashTxt = usrTxt.substring(usrTxt.indexOf("id:") + 3, usrTxt.indexOf(",name") );

pId = Integer.valueOf(trashTxt);

// System.out.print("ID:" + pId);


trashTxt = usrTxt.substring(usrTxt.indexOf("name:") + 5, usrTxt.indexOf(",value") );

pName = trashTxt;

// System.out.print(",name:" + pName);

trashTxt = usrTxt.substring(usrTxt.indexOf("value:") + 6, usrTxt.indexOf("/") );

pValue = trashTxt;

// System.out.println(",value:" + pValue);

}

}

return null;

}


// ReqInfoList 내용 - 문맥 문석기(임시)

public static Object getReqinfoParser(List<String> reqNode, String tarName) {

int pId;

String pName ;

Object pValue ;

String trashTxt;

if ( reqNode != null) {

for ( String usrTxt : reqNode  ) {

trashTxt = usrTxt.substring(usrTxt.indexOf("id:") + 3, usrTxt.indexOf(",name") );

pId = Integer.valueOf(trashTxt);

System.out.print("ID:" + pId);


trashTxt = usrTxt.substring(usrTxt.indexOf("name:") + 5, usrTxt.indexOf(",value") );

pName = trashTxt;

System.out.print(",name:" + pName);

trashTxt = usrTxt.substring(usrTxt.indexOf("value:") + 6, usrTxt.indexOf("/") );

pValue = trashTxt;

System.out.println(",value:" + pValue);

}

}

return null;

}

}


파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil.zip




11. Model - CompUsers.java


모델은 DB의 테이블 설계를 생각해서 하는 것이 좋다고 본다. (꼭 그렇다는 건 아님.)


아래의 그림 16, 그림 17, 그림 18은 결국은 같은 Table에 대한 것이다.

하나를 바라보는 것이 다양할 수 있다는 이야기를 하는 것이다.



그림 16. Oracle SQL Developer - COMP_USERS (Tables)



그림 17. Oracle SQL Developer - COMP_USERS (Tables)



그림 18. Oracle SQL Developer - COMP_USERS (Tables)


비고: 이 글에서는 DB 설계는 하지 않았음.



경로: /src/main/java/com/example/web/model/CompUsers.java


package com.example.web.model;


public class CompUsers {


private String username;

private String password;

private int enabled;

public CompUsers(String username, String password, int enabled) {

this.username = username;

this.password = password;

this.enabled = enabled;

}


public CompUsers() {

}


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 int getEnabled() {

return enabled;

}

public void setEnabled(int enabled) {

this.enabled = enabled;

}

}



파일명: CompUsers.java


[첨부(Attachments)]

CompUsers.zip



* 2부에서는 Jquery 다운받기, JSON, MultiUploadController, View 화면, Index 페이지 구성에 대해서 소개하겠다.


1. [JSP] 25. jQuery와 Ajax 멀티 파일 업로드(개선), POST전송, JSON - 구성하기(2), 2020-10-03

https://yyman.tistory.com/1446


반응형
728x90
300x250

[JSP] 22. JSP/Servlet MVC2 - jQuery기반의 Ajax POST 전송 및 MVC패턴 Action 식별자


jQuery를 활용하는 방법에 대해서 소개하려고 한다.

그림 일기 수준으로 매우 편안한 마음으로 입문할 수 있는 학습 기반을 제공해주려고 한다.

심도적인 부분은 전문서적 등도 찾아보기도 하고, 검색도 많이 해보시면 좋을 듯 싶다. (변화가 워낙 빠른 분야인 만큼)



1. 결과


동적인 부분인 관계로 영상으로 올려주도록 하겠다.



영상 1. 출력 결과


jQuery가 어떤 결과물을 만들어 내는 도구인지 이해가 되었으면 하는 바램이다.

원래는 jQuery가 먼저 나온 것이 아니고, Javascript가 먼저이다.


그리고 2006년도에 약 Ajax라는 규격이 나왔는데, Javascript 파일 형태로 ajax 파일을 배포하여 동적인 처리에 대해서 접근하게 되었다.


그리고 조금 시간이 지나서 jQuery라는 게 나왔는데, 분명한 건 이건 표준은 아니다.

다만, 편하게 사용하도록 도와주는 오픈소스 진형의 library이다.




2. 프로젝트 생성하기


이러한 저차원 수준으로 작성한 이유는 프로그래밍에 겁먹지 말라는 것이다.



그림 1. 프로젝트 생성하기


File -> New -> Maven Project를 클릭한다.



그림 2. 프로젝트 생성하기


org.apache.maven.archetypes | maven-archetype-webapp를 선택한다.

Next를 클릭한다.


검색 키워드로 검색할 때, "webapp"로 하면, 조금 찾는데 시간이 절약될 수 있다.

다만, HDD 사용자는 조금 버벅될 가능성이 있다. (검색 기능이 매우 느림.)




그림 3. 프로젝트 생성하기


GroupId, Artifact Id를 적당히 입력한 후 Finish를 누른다.



3. Pom.xml - 설정하기


http://mvnrepository.com에 들어가서 "servlet"을 검색해서 최신 버전으로 해준다.

Maven Project를 아끼고 있는 이유는 정말 사용하기가 편리하다.


셋팅하는데, /src/main/webapp/WEB-INF/lib인가 폴더에 넣고, 프로젝트 속성에 들어가서 BuildPath 클릭하고 Add External Library 등록하고 

확인 몇 번하고 복잡했는데, Maven을 알게 되면서 많이 좋아진 것 같다.



그림 4. pom.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/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>


  <groupId>com.webedu</groupId>

  <artifactId>exampleAjax</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>war</packaging>


  <name>exampleAjax Maven Webapp</name>

  <!-- FIXME change it to the project's website -->

  <url>http://www.example.com</url>


  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <maven.compiler.source>1.7</maven.compiler.source>

    <maven.compiler.target>1.7</maven.compiler.target>

  </properties>


  <dependencies>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.11</version>

      <scope>test</scope>

    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>

  </dependencies>


  <build>

    <finalName>exampleAjax</finalName>

    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->

      <plugins>

        <plugin>

          <artifactId>maven-clean-plugin</artifactId>

          <version>3.1.0</version>

        </plugin>

        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->

        <plugin>

          <artifactId>maven-resources-plugin</artifactId>

          <version>3.0.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-compiler-plugin</artifactId>

          <version>3.8.0</version>

        </plugin>

        <plugin>

          <artifactId>maven-surefire-plugin</artifactId>

          <version>2.22.1</version>

        </plugin>

        <plugin>

          <artifactId>maven-war-plugin</artifactId>

          <version>3.2.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-install-plugin</artifactId>

          <version>2.5.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-deploy-plugin</artifactId>

          <version>2.8.2</version>

        </plugin>

      </plugins>

    </pluginManagement>

  </build>

</project>



파일명: pom.xml


[첨부(Attachments)]

pom.zip




4. Project Factes와 Build Path - 자바 버전 바꿔주기


자바 버전을 변경해줘야 한다. Maven Project 초기 셋팅이 현재 1.6으로 잡혀서 생성되기 때문이다.



그림 5. 프로젝트의 오른쪽 메뉴 모습


프로젝트를 선택한 후, 마우스 오른쪽 버튼을 클릭한다.

Properties를 클릭한다.



그림 6. Build-Path


JRE System Library [JavaSE-14]로 바꿔준다.

Edit 버튼을 누르면 바꿀 수 있다.



그림 7. Project Factes


Java의 버전을 14로 바꿔준다.




5. jQuery 사이트 - 접속하기


인터넷 웹 브라우저를 켠다.


그림 8. 윈도우 10 - 엣지 클릭하기


웹 브라우저 아이콘을 못 찾는 경우에는 그림처럼 생긴 거 클릭하면 된다. 

(그 옆에도 비슷하게 생긴 e자 아이콘을 클릭해도 무방하다.)



그림 11. jQuery 웹 사이트 접속하기


https://jquery.com/download


링크를 웹 주소창에 입력해서 접속한다.

또는 클릭해도 무방하다.


표기해놓은 것 5개 중 하나만 잘 다운로드 받으면 된다.




그림 12. jQuery 다운받기


문서 클릭\workspace-spring-tool-suite-4-4.7.2.RELEASE\{프로젝트명}\src\main\webapp\js에 저장해준다.

js폴더가 없으니깐 하나 만들어준다.

js폴더 안에 저장해준다.



영상 2. jQuery 다운로드 받는 모습



6. jQuery - ajaxPost - API 접속하기


https://api.jquery.com/jquery.post/


사이트에 접속해준다.



그림 13. jQuery 예제


이렇게 생긴 HTML태그를 복사하여 Eclipse에 옮겨갈 것이다.

참고로 이 코드가 완벽한 코드는 절대 아니다. (태스트 해 봤음.)




그림 14. index.jsp 파일에 복사 붙여넣기 해주기


그림 14와는 다르게 태스트를 완료한 코드는 아래에 있으니 참고하면 되겠다.

WEB-INF내에 views폴더 생성해준다.

그리고 index.jsp 파일을 views 폴더 안으로 이동해준다.



영상 3. views 폴더 생성 방법 및 파일 이동 시키기



그림 15. 이동을 완료한 모습 - views 폴더 내용


<!doctype html>

<html lang="en">

<head>

<meta charset="utf-8">

<title>jQuery.post demo</title>

<script src="js/jquery-3.5.1.js"></script>


</head>

<body>

 

<form action="board/insert.do" id="searchForm">

  <input type="text" name="s" placeholder="Search...">

  <input type="submit" value="Search">

</form>

<div id="result"></div>


<script type="text/javascript">

// Attach a submit handler to the form

$( "#searchForm" ).submit(function( event ) {

 

  // Stop form from submitting normally

  event.preventDefault();

 

// Get some values from elements on the page:

  var $form = $( this ),

    term = $form.find( "input[name='s']" ).val(),

    url = $form.attr( "action" );

 

  // Send the data using post

  var posting = $.post( url, { s: term } );

 

  // Put the results in a div

  posting.done(function( data ) {

    alert( "Data Loaded: " + data );

    var content = data ;

    $( "#result" ).html( content );

   

  });

 

});


</script>

<!-- the result of the search will be rendered inside this div --> 

 

</body>

</html>


파일명: index.jsp


[첨부(Attachments)]

index.zip




7. Servlet 작성하기


MVC2 패턴을 만들거니깐 서블렛을 잘 따라 해야 한다.



그림 16. Java Resources 폴더 - 마우스 오른쪽 버튼 메뉴 모습


Java Resources 폴더를 선택한 후, 마우스 오른쪽 버튼으로 클릭한다.

New-> Servlet을 클릭한다.



그림 17. Servlet 생성 작업창 - 모습 (Create Servlet)


Java Package명을 입력한다. (예: com.example.web.controller)

Class Name명을 입력한다. (예: FrontController)


Finish를 누른다.



8. web.xml 파일 - 수정작업하기


경로 위치: /src/main/webapp/WEB-INF/web.xml


문자열하고, FrontController의 *.do로 변경 작업을 해주면 된다.



그림 18. web.xml - 수정작업하기


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

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

xmlns="http://java.sun.com/xml/ns/javaee" 

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 

id="WebApp_ID" version="3.0">

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

  <servlet>

  <servlet-name>FrontController</servlet-name>

  <servlet-class>com.example.web.controller.FrontController</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet-mapping>

  <servlet-name>FrontController</servlet-name>

  <url-pattern>*.do</url-pattern>

  </servlet-mapping>

</web-app>



파일명: web.xml


[첨부(Attachments)]

web.zip




9. Class 만들기 - HttpUtil


HttpUtil 클래스를 만들어주려고 한다.



그림 19. Java Resources의 오른쪽 버튼 메뉴 모습


Java Resources를 마우스 오른쪽 버튼으로 클릭한다.

New->Class를 클릭한다.



그림 20. 클래스 생성 마법사 모습


Package명은 com.example.web.util

Name은 HttpUtil로 한다.


Finish를 누른다.





그림 21. 코드를 입력한 모습 - HttpUtil.java


package com.example.web.util;


import java.io.IOException;


import javax.servlet.RequestDispatcher;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class HttpUtil extends HttpServlet {


private static final long serialVersionUID = 1L;


private static String charset = null;


public static void forward(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {


try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);


}catch(Exception e) {

e.printStackTrace();

}

}

}


파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil.zip



10. Controller - Interface 만들기


Controller 인터페이스를 하나 만들 것이다.



그림 22. Controller.java


package com.example.web.controller;


import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public interface Controller {


public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;


}




파일명: Controller.java


[첨부(Attachments)]

Controller.zip



11. InsertController.java - Class


앞서 만든 Interface(Controller)로 InsertController.java를 작성할 것이다.



그림 23. InsertController.java


Package 명은 com.example.web.board라고 입력한다.

Name명은 InsertController라고 입력한다.


Interfaces의 Add버튼을 누른다.



그림 24. Implemented Interfaces.......


Controller를 입력한 후 작성한 Controller 인터페이스를 선탁한다.

OK를 누른다.

Finish를 누른다.


package com.example.web.controller.board;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import com.example.web.controller.Controller;

import com.example.web.util.HttpUtil;


public class InsertController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

HttpUtil.forward(req, res, "/WEB-INF/views/board/insert.jsp");


}


}



파일명: InsertController.java


[첨부(Attachments)]

InsertController.zip



13. FrontController - 코드 작성하기 (수정)


코드를 자세히 보면, flag가 추가되었다.

doAction에서 POST, GET 등을 인식할 수 있게 되었다.


package com.example.web.controller;


import java.io.IOException;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import com.example.web.controller.board.InsertController;

import com.example.web.util.HttpUtil;


public class FrontController extends HttpServlet {

private static final long serialVersionUID = 1L;

private String charset = null;

       

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

doAction(req, res, "GET");

}


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

       doAction(req, res, "POST");

}


protected void doAction(HttpServletRequest req, 

HttpServletResponse res,

String flag) 

throws ServletException, IOException {

ServletConfig sc = this.getServletConfig();

charset = sc.getInitParameter("charset");


req.setAttribute("charset", charset);

req.setCharacterEncoding(charset);

res.setContentType("text/html; charset=" + charset);


String uri = req.getRequestURI();

String conPath = req.getContextPath();

String command = uri.substring(conPath.length());


Controller subController = null;


// POST 전송방식만

if(command.equals("/board/insert.do") && 

flag.contentEquals("POST")){


System.out.println("insert");

System.out.println("----------------");

subController = new InsertController();

subController.execute(req, res);

}else if (command.equals("/index.do")){

HttpUtil.forward(req, res, "/WEB-INF/views/index.jsp");

}

}

}



파일명: FrontController.java


[첨부(Attachments)]

FrontController.zip



14. View - webapp/WEB-INF/views/board/insert.jsp


POST처리 후에 출력할 페이지를 작성하도록 하겠다.

PrintWriter out으로 해도 무방하지만, MVC패턴을 적용했으니 뷰 파일에서 작성하겠다.


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

    pageEncoding="UTF-8"%>

<%

String text = (String)request.getParameter("s");

%>

<%= text %>


파일명: insert.jsp


[첨부(Attachments)]

insert.zip



* 맺음글(Conclusion)


jQuery를 통해서 ajax 전송에 대해서 간단한 형태로 살펴보았다.

반응형
728x90
300x250

[Spring-F.] 29. MyBatis 3.5, Spring Framework 5.4, Oracle 19g - 방법5(SqlMap Spring전용)


소스 코드 위주로 소개하겠다.


[실험 결과]

1. [Spring-Framework] Spring Framework 5.4, MyBatis 3.5 연동 방법 - 실험 결과, 2020-10-02

   - https://yyman.tistory.com/1437


[이전 게시글]

1. [Spring-F.] 28. MyBatis 3.5, Spring Framework 5.4, Oracle 19g - 방법4(SqlMap Spring전용)

   - https://yyman.tistory.com/1441



41. 결과


XML 방식의 XML Mapping으로 구성하여 동작한 결과이다.



그림 25. 결과 5번



그림 26. 프로젝트 구성도



42. web.xml - 기본값


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">


<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/spring/root-context.xml

</param-value>

</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<!-- Processes application requests -->

<servlet>

<servlet-name>appServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/spring/appServlet/servlet-context.xml

</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>appServlet</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


</web-app>





43. com.example.spbatis.db - SqlMapSessionFactory.java


package com.example.spbatis.db;



import java.io.IOException;

import java.io.InputStream;

import java.sql.SQLException;


import javax.sql.DataSource;


import org.apache.ibatis.io.Resources;

import org.apache.ibatis.mapping.Environment;

import org.apache.ibatis.session.Configuration;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import org.apache.ibatis.transaction.TransactionFactory;

import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import org.mybatis.spring.SqlSessionFactoryBean;

import org.springframework.core.io.support.PathMatchingResourcePatternResolver;


import com.example.spbatis.model.CompUsers;


import oracle.jdbc.pool.OracleDataSource;


public class SqlMapSessionFactory {


private static SqlMapSessionFactory factory = new SqlMapSessionFactory();

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

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

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

private final static String userPassword = "비밀번호";

public static SqlSessionFactory ssf;


public static SqlMapSessionFactory getInstance() {

return factory;

}

private SqlMapSessionFactory () {    }

static {

/* 방법1

DataSource dataSource = getOracleDataSource();

TransactionFactory transactionFactory = new JdbcTransactionFactory();

Environment environment = new Environment("development", transactionFactory, dataSource);

Configuration configuration = new Configuration(environment);

configuration.addMapper(CompUsersMapper.class);

ssf = new SqlSessionFactoryBuilder().build(configuration);

*/

}

    

    public static SqlSessionFactory getSqlSessionFactory(){

   


/* 방법2: 공식 메뉴얼 참고 */

DataSource dataSource = getOracleDataSource();


SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); // Spring Framework만 지원

factoryBean.setDataSource(dataSource);

try {

//쿼리가 정의된 xml 파일들의 위치 지정

        factoryBean.setMapperLocations(

                new PathMatchingResourcePatternResolver().getResources("com/example/spbatis/mapper/*.xml")

        );

    

ssf = factoryBean.getObject();

// 클래스 등록

// ssf.getConfiguration().addMapper(CompUsersMapper.class);

} catch (Exception e) {

e.printStackTrace();

}

   

    // 방법 1, 방법 2 공통

        return ssf;

    }

    

    /*

* Description: 순정 오라클 데이터소스

*/

    private static 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;


    }

    

}



파일명: SqlMapSessionFactory.java


[첨부(Attachments)]

SqlMapSessionFactory.zip




44. com.example.spbatis.mapper - CompUsersMapper.xml


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

<!DOCTYPE mapper

  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.example.spbatis.mapper">


<select id="allCompUsers" resultType="com.example.spbatis.model.CompUsers">

select * from comp_users

</select>

<!-- 

<select id="allAddress" resultType="com.edu.db.AddressDto">

select * from addressbook

</select>

<select id="selectAddress" parameterType="Integer" resultType="com.edu.db.AddressDto">

select NUM, NAME, ADDRESS, BIRTHDATE

from addressbook

  where num=#{num}

</select>

<insert id="insertAddress" parameterType="com.edu.db.AddressDto">

insert into

addressbook(NAME, ADDRESS, BIRTHDATE)

values

(#{name},#{address},#{birthdate})

</insert>

<delete id="deleteAddress" parameterType="Integer">

DELETE FROM AddressBook

WHERE NUM = #{num}

</delete>

<update id="updateAddress" parameterType="com.edu.db.AddressDto" >

update addressbook

set birthdate = #{birthdate}, name = #{name}, address =#{address}

where num = #{num}

</update>

 -->

</mapper>


파일명: CompUsersMapper.xml


[첨부(Attachments)]

CompUsersMapper.zip





45. com.example.spbatis.dao - CompUsersDao.java


package com.example.spbatis.dao;


import java.util.List;


import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;


import com.example.spbatis.db.SqlMapSessionFactory;

import com.example.spbatis.model.CompUsers;



public class CompUsersDao {


    SqlSessionFactory factory = SqlMapSessionFactory.getSqlSessionFactory();

    

    public CompUsersDao() {

        

    }

    

    // SQL 세션 열기

    public List<CompUsers> selectAddress() {


    List<CompUsers> user = null;

try (SqlSession session = factory.openSession()) {

 

user = session.selectList("com.example.spbatis.mapper.allCompUsers");

session.close();

// System.out.println("계정명:" + user.getUsername());


}

return user;


    }

    

}



파일명: CompUsersDao.java


[첨부(Attachments)]

CompUsersDao.zip





46. com.example.spbatis.service - CompUsersService.java


package com.example.spbatis.service;


import java.util.List;


import com.example.spbatis.dao.CompUsersDao;

import com.example.spbatis.model.CompUsers;


public class CompUsersService {


private CompUsersDao dao = null;

public CompUsersService() {

dao = new CompUsersDao();

}

public List<CompUsers> getAllCompUsers() {

return dao.selectAddress();

}

 

}



파일명: CompUsersService.java


[첨부(Attachments)]

CompUsersService.zip




47. com.example.spbatis - HomeController.java


package com.example.spbatis;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.example.spbatis.service.CompUsersService;


/**

 * Handles requests for the application home page.

 */

@Controller

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

private CompUsersService compUserService;

@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home5 - Java(XML Mappers)! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

compUserService = new CompUsersService();

model.addAttribute("serverTime", formattedDate );

model.addAttribute("list", compUserService.getAllCompUsers()) ;

return "home";

}

}



파일명: HomeController.java


[첨부(Attachments)]

HomeController.zip





48. view - home.jsp


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

    pageEncoding="UTF-8"%>

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

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

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

<%@ page session="false" %>


<html>

<head>

<title>Home</title>

</head>

<body>

<h1>

Hello world!  

</h1>


<P>  The time on the server is ${serverTime}. </P>

<h3>SqlMapSessionFactory 방식(Java - XML Mapper - Spring 전용 버전)</h3>

<c:forEach items="${list }" var="val" >

    ${val.username} / 

    ${val.password} /

    ${val.enabled }<br>

</c:forEach>

<c:if test="${empty list }">

    ${"데이터가 존재하지않아요."}

</c:if>


</body>

</html>



파일명: home.jsp


[첨부(Attachments)]

home.zip



* 맺음글(Conclusion)


조금 작성하는데 힘이 들었지만. MyBatis 하나 셋팅에는 다양한 방법이 지원된다는 것을 알 수 있었다.



* 참고 자료(References)


1. mybatis-spring - 마이바티스 스프링 연동모듈 | 시작하기, https://mybatis.org/spring/ko/getting-started.html, Accessed by 2020-10-02, Last Modified 2020-06-05.

   [비고] - 공식 사이트이다. (이 메뉴얼만 가지고는 다 안 될 수도 있다.)


2. 스프링프레임워크 Java config로 MyBatis 사용하기, https://offbyone.tistory.com/381, Accessed by 2020-10-02, Last Modified 2019-03-17.


3. [mybatis] 다중 Database Setting, https://wfreud.tistory.com/310, Accessed by 2020-10-02, Last Modified 2019-03-21.


4. maven프로젝트 src/main/resource경로의 파일 읽기, https://blog.naver.com/down83/50092131189, Accessed by 2020-10-02, Last Modified 2010-07-13.


5. MyBatis List와 Map으로 데이터 불러오기, https://smujihoon.tistory.com/112, Accessed by 2020-10-02, Last Modified 2019-02-11.


5. [Mybatis] root-context.xml 자료(Oracle), https://datamod.tistory.com/81, Accessed by 2020-10-02, Last Modified 2018-05-20.


6. 7. 공용 클래스 작성 - commonDAo, https://tapasnote.tistory.com/21?category=697717, Accessed by 2020-10-02, Last Modified 2017-04-12.


7. DatsSource, 커넥션 풀을 이용한 DB 연결, https://m.blog.naver.com/PostView.nhn?blogId=2hyoin&logNo=220647895962, Accessed by 2020-10-02, Last Modified 2016-03-07.


8. MyBatis tutorial - Introductory MyBatis tutorial, http://zetcode.com/db/mybatis/, Accessed by 2020-10-02, Last Modified 2020-07-06.

   [영어 사이트]


반응형
728x90
300x250

[Spring-F.] 28. MyBatis 3.5, Spring Framework 5.4, Oracle 19g - 방법4(SqlMap Spring전용)


소스 코드 위주로 소개하도록 하겠다.


[실험 결과]

1. [Spring-Framework] Spring Framework 5.4, MyBatis 3.5 연동 방법 - 실험 결과, 2020-10-02

   - https://yyman.tistory.com/1437


[이전 게시글]

1. [Spring-F.] 27. MyBatis 3.5, Spring Framework 5.4, Oracle 19g - 방법3(SqlMap-XML-Class)     // 결과: 지원, 2020-10-02

    - https://yyman.tistory.com/1440



33. 결과


작업한 내용을 출력한 결과는 다음과 같다.



그림 23. 결과 모습(Java 방식) - XML Mapper - Spring Beans 객체 방식 적용 (Spring 전용)



그림 24. 프로젝트 구성도




34. 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">


<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/spring/root-context.xml

</param-value>

</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<!-- Processes application requests -->

<servlet>

<servlet-name>appServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/spring/appServlet/servlet-context.xml

</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>appServlet</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


</web-app>





35. com.example.spbatis.db - SqlMapSessionFactory.java


package com.example.spbatis.db;



import java.io.IOException;

import java.io.InputStream;

import java.sql.SQLException;


import javax.sql.DataSource;


import org.apache.ibatis.io.Resources;

import org.apache.ibatis.mapping.Environment;

import org.apache.ibatis.session.Configuration;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import org.apache.ibatis.transaction.TransactionFactory;

import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import org.mybatis.spring.SqlSessionFactoryBean;

import org.springframework.core.io.support.PathMatchingResourcePatternResolver;


import com.example.spbatis.mapper.CompUsersMapper;

import com.example.spbatis.model.CompUsers;


import oracle.jdbc.pool.OracleDataSource;


public class SqlMapSessionFactory {


private static SqlMapSessionFactory factory = new SqlMapSessionFactory();

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

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

private final static String userName = "사용자계정";

private final static String userPassword = "비밀번호";

public static SqlSessionFactory ssf;


public static SqlMapSessionFactory getInstance() {

return factory;

}

private SqlMapSessionFactory () {    }

static {

/* 방법1

DataSource dataSource = getOracleDataSource();

TransactionFactory transactionFactory = new JdbcTransactionFactory();

Environment environment = new Environment("development", transactionFactory, dataSource);

Configuration configuration = new Configuration(environment);

configuration.addMapper(CompUsersMapper.class);

ssf = new SqlSessionFactoryBuilder().build(configuration);

*/

}

    

    public static SqlSessionFactory getSqlSessionFactory(){

   


/* 방법2: 공식 메뉴얼 참고 */

DataSource dataSource = getOracleDataSource();


SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); // Spring Framework만 지원

factoryBean.setDataSource(dataSource);

try {

//쿼리가 정의된 xml 파일들의 위치 지정

/*

        factoryBean.setMapperLocations(

                new PathMatchingResourcePatternResolver().getResources("com/example/spbatis/mapper/*.xml")

        );

        */

    

ssf = factoryBean.getObject();

// 클래스 등록

ssf.getConfiguration().addMapper(CompUsersMapper.class);

} catch (Exception e) {

e.printStackTrace();

}

   

    // 방법 1, 방법 2 공통

        return ssf;

    }

    

    /*

* Description: 순정 오라클 데이터소스

*/

    private static 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;


    }

    

}



파일명: SqlSessionFactory.java


[첨부(Attachments)]

SqlMapSessionFactory.zip





36. com.example.spbatis.mapper - CompUsersMapper.java


package com.example.spbatis.mapper;


import java.util.List;


import org.apache.ibatis.annotations.Mapper;

import org.apache.ibatis.annotations.Select;


import com.example.spbatis.model.CompUsers;


@Mapper

public interface CompUsersMapper {

@Select("SELECT * FROM comp_users")

List<CompUsers> selectAll();

}


파일명: CompUsersMapper.java


[첨부(Attachments)]

CompUsersMapper.zip





37. com.example.spbatis.dao - CompUsersDao.java


package com.example.spbatis.dao;


import java.util.List;


import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

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


import com.example.spbatis.db.SqlMapSessionFactory;

import com.example.spbatis.mapper.CompUsersMapper;

import com.example.spbatis.model.CompUsers;



public class CompUsersDao {


    SqlSessionFactory factory = SqlMapSessionFactory.getSqlSessionFactory();

    

    public CompUsersDao() {

        

    }

    

    // SQL 세션 열기

    public List<CompUsers> selectAddress() {


    List<CompUsers> user = null;

try (SqlSession session = factory.openSession()) {

CompUsersMapper mapper = session.getMapper(CompUsersMapper.class);

user = mapper.selectAll();

// System.out.println("계정명:" + user.getUsername());


}

return user;


    }

    

}



파일명: CompUsersDao.java


[첨부(Attachments)]

CompUsersDao.zip





38. com.example.spbatis.service - CompUsersService.java


package com.example.spbatis.service;


import java.util.List;


import com.example.spbatis.dao.CompUsersDao;

import com.example.spbatis.model.CompUsers;


public class CompUsersService {


private CompUsersDao dao = null;

public CompUsersService() {

dao = new CompUsersDao();

}

public List<CompUsers> getAllCompUsers() {

return dao.selectAddress();

}

 

}



파일명: CompUsersService.java


[첨부(Attachments)]

CompUsersService.zip





39. com.example.spbatis - HomeController.java


package com.example.spbatis;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.example.spbatis.service.CompUsersService;


/**

 * Handles requests for the application home page.

 */

@Controller

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

private CompUsersService compUserService;

@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home4(Class Mappers) - Java! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

compUserService = new CompUsersService();

model.addAttribute("serverTime", formattedDate );

model.addAttribute("list", compUserService.getAllCompUsers()) ;

return "home";

}

}



파일명: HomeController.java


[첨부(Attachments)]

HomeController.zip



40. View - Home.jsp


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

    pageEncoding="UTF-8"%>

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

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

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

<%@ page session="false" %>


<html>

<head>

<title>Home</title>

</head>

<body>

<h1>

Hello world!  

</h1>


<P>  The time on the server is ${serverTime}. </P>

<h3>SqlMapSessionFactory 방식(Java - Mapper - Spring 전용 버전)</h3>

<c:forEach items="${list }" var="val" >

    ${val.username} / 

    ${val.password} /

    ${val.enabled }<br>

</c:forEach>

<c:if test="${empty list }">

    ${"데이터가 존재하지않아요."}

</c:if>


</body>

</html>



파일명: home.jsp


[첨부(Attachments)]

home.zip




* 5부에서 만나요.


1. [Spring-F.] 29. MyBatis 3.5, Spring Framework 5.4, Oracle 19g - 방법5(SqlMap Spring전용)  // 결과: 지원, 2020-10-02

    - https://yyman.tistory.com/1442



반응형
728x90
300x250

[Spring-F.] 27. MyBatis 3.5, Spring Framework 5.4, Oracle 19g - 방법3(SqlMap-XML-Class)


소스 코드 위주로 소개하도록 하겠다.


[실험 결과]

1. [Spring-Framework] Spring Framework 5.4, MyBatis 3.5 연동 방법 - 실험 결과, 2020-10-02

   - https://yyman.tistory.com/1437


[이전 게시글]

1. [Spring-F.] 26. MyBatis 3.5, Spring Framework 5.4, Oracle 19g - 방법2(SqlMap-XML-XML 맵핑)     // 결과: 지원, 2020-10-02

    - https://yyman.tistory.com/1439



24. 결과


완성된 실험 결과이다.



그림 21. 결과



그림 22. 프로젝트 구성도




25. 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">


<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/spring/root-context.xml

</param-value>

</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>


<!-- Processes application requests -->

<servlet>

<servlet-name>appServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/spring/appServlet/servlet-context.xml

</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>appServlet</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


</web-app>




26. view - home.jsp


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

    pageEncoding="UTF-8"%>

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

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

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

<%@ page session="false" %>


<html>

<head>

<title>Home</title>

</head>

<body>

<h1>

Hello world!  

</h1>


<P>  The time on the server is ${serverTime}. </P>

<h3>SqlMapSessionFactory 방식(XML - 환경설정 - 클래스-Mappers)</h3>

<c:forEach items="${list }" var="val" >

    ${val.username} / 

    ${val.password} /

    ${val.enabled }<br>

</c:forEach>

<c:if test="${empty list }">

    ${"데이터가 존재하지않아요."}

</c:if>


<h3>XML - web.xml 설정방식</h3>

<c:forEach items="${list2 }" var="val" >

    ${val.username} / 

    ${val.password} /

    ${val.enabled }<br>

</c:forEach>

<c:if test="${empty list2 }">

    ${"데이터가 존재하지않아요."}

</c:if>


</body>

</html>



파일명: home.jsp


[첨부(Attachments)]

home.zip




27. com.example.spbatis.db - SqlMapSessionFactory.java


package com.example.spbatis.db;



import java.io.IOException;

import java.io.InputStream;


import org.apache.ibatis.io.Resources;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;


import com.example.spbatis.mapper.CompUsersMapper;


public class SqlMapSessionFactory {

public SqlSessionFactory ssf;

public SqlMapSessionFactory () {


        String resource = "com/example/spbatis/mapper/mybatis-config.xml";

        InputStream is = null;


        try {

            is = Resources.getResourceAsStream(resource);

        } catch (IOException e) {

            e.printStackTrace();

        }


        ssf = new SqlSessionFactoryBuilder().build(is);

        ssf.getConfiguration().addMapper(CompUsersMapper.class);


    }

    

    public SqlSessionFactory getSqlSessionFactory(){

        return ssf;

    }

    

}



파일명: SqlMapSessionFactory.java


[첨부(Attachments)]

SqlMapSessionFactory.zip





28. com.example.spbatis.mapper - mybatis-config.xml


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


<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 

"http://mybatis.org/dtd/mybatis-3-config.dtd">


<configuration>


<environments default="development">

    <environment id="development">

      <transactionManager type="JDBC"/>

      <!-- 커넥션 풀(MyBatis) -->

      <dataSource type="POOLED">

        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />

        <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>

        <property name="username" value="UserName"/>

        <property name="password" value="Password"/>

        <property name="poolMaximumActiveConnections" value="20"/>

        <property name="poolMaximumIdleConnections" value="20"/>

        <property name="poolMaximumCheckoutTime" value="20000"/>

        <property name="poolPingEnabled" value="true"/>

        <property name="poolPingQuery" value="select 1"/>

        <property name="poolPingConnectionsNotUsedFor" value="10000"/>

        <property name="poolTimeToWait" value="15000"/>

      </dataSource>

    </environment>

</environments>


</configuration>


파일명: mybatis-config.xml


[첨부(Attachments)]

mybatis-config.zip




29. com.example.spbatis.mapper - CompUsersMapper.java


package com.example.spbatis.mapper;


import java.util.List;


import org.apache.ibatis.annotations.Mapper;

import org.apache.ibatis.annotations.Select;


import com.example.spbatis.model.CompUsers;


@Mapper

public interface CompUsersMapper {

@Select("SELECT * FROM comp_users")

List<CompUsers> selectAll();

}


파일명: CompUsersMapper.java


[첨부(Attachments)]

CompUsersMapper.zip




30. com.example.spbatis.dao - CompUsersDao.java


package com.example.spbatis.dao;


import java.util.List;


import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

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


import com.example.spbatis.db.SqlMapSessionFactory;

import com.example.spbatis.mapper.CompUsersMapper;

import com.example.spbatis.model.CompUsers;



public class CompUsersDao {

    private SqlMapSessionFactory ssfc;

    private SqlSessionFactory factory;

    

    public CompUsersDao() {

   

        ssfc = new SqlMapSessionFactory();

        factory = ssfc.getSqlSessionFactory();

        

    }

    

    // SQL 세션 열기

    public List<CompUsers> selectAddress() {


    List<CompUsers> user = null;

   

try (SqlSession session = factory.openSession()) {

CompUsersMapper mapper = session.getMapper(CompUsersMapper.class);

user = mapper.selectAll();

// System.out.println("계정명:" + user.getUsername());


}

return user;


    }

    

}



파일명: CompUsersDao.java


[첨부(Attachments)]

CompUsersDao.zip





31. com.example.spbatis.service - CompUsersService.java


package com.example.spbatis.service;


import java.util.List;


import com.example.spbatis.dao.CompUsersDao;

import com.example.spbatis.model.CompUsers;


public class CompUsersService {


private CompUsersDao dao = null;

public CompUsersService() {

dao = new CompUsersDao();

}

public List<CompUsers> getAllCompUsers() {

return dao.selectAddress();

}

 

}



파일명: CompUsersService.java


[첨부(Attachments)]

CompUsersService.zip





32. com.example.spbatis - HomeController.java


package com.example.spbatis;


import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


import com.example.spbatis.service.CompUsersService;


/**

 * Handles requests for the application home page.

 */

@Controller

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

private CompUsersService compUserService;

@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home3! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

compUserService = new CompUsersService();

model.addAttribute("serverTime", formattedDate );

model.addAttribute("list", compUserService.getAllCompUsers()) ;

return "home";

}

}



파일명: HomeController.java


[첨부(Attachments)]

HomeController.zip



* 4부에서 만나요.


1. [Spring-F.] 28. MyBatis 3.5, Spring Framework 5.4, Oracle 19g - 방법4(SqlMap Spring전용)   // 결과: 지원, 2020-10-02

https://yyman.tistory.com/1441


반응형
728x90
300x250

[JSP] 21. Jsp/Servlet(MVC) Maven 기반의 다중 업로드, 다운로드, 삭제 구현(1)


이 글의 소스코드를 수정해서 삭제 기능까지 추가해보도록 하겠다.


1. [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1)

https://yyman.tistory.com/1414

2. [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2)

https://yyman.tistory.com/1415



1. 결과


입력부터 다운로드, 삭제에 이르는 전 과정을 담아보았다.



그림 1. 다중 업로드 작업




그림 2. 다중 업로드된 모습





그림 3. 파일을 찾을 수 없을 때 반응




그림 4. 다운로드 모습




그림 5. 삭제 모습



그림 6. 파일이 다 지워질 경우에 폴더 삭제까지 하기



그림 7. 프로젝트 구성도(변경됨)




2. 변경 - Controller (FrontController.java)


package com.fileWeb.controller;


import java.io.IOException;

import java.util.HashMap;

import java.util.Map;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;


import com.fileWeb.util.HttpUtil;


/**

 * Servlet implementation class HomeController

 */

public class FrontController extends HttpServlet {

private static final long serialVersionUID = 1L;

       

private String charset = null;

    public FrontController() {

        super();

    }


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

doAction(req, res);

}

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

doAction(req, res);

}

// FrontController 패턴 & Command 패턴

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


ServletConfig sc = this.getServletConfig();

charset = sc.getInitParameter("charset");

req.setAttribute("charset", charset);

req.setCharacterEncoding(charset);

res.setContentType("text/html; charset=" + charset);

System.out.println(charset);

String uri = req.getRequestURI();

System.out.println("uri : " + uri);

String conPath = req.getContextPath();

System.out.println("conPath : " + conPath);

String command = uri.substring(conPath.length());

System.out.println("command : " + command);

Controller subController = null;

System.out.println("reqMapSize : " + req.getParameterMap().size());


if(command.equals("/board/insert.do")){

System.out.println("insert");

System.out.println("----------------");


    subController = new BoardInsertController();

    subController.execute(req, res);

   

}else if (command.equals("/board/insertResult.do")) {

System.out.println("insertResult");

System.out.println("----------------");


    subController = new BoardInsertResultController();

subController.execute(req, res);

}

else if(command.equals("/board/insertMultiResult.do")){

System.out.println("insertResult");

System.out.println("----------------");


    subController = new BoardInsertMultiResultController();

subController.execute(req, res);

}else if(command.equals("/board/download.do")) {

System.out.println("download");

System.out.println("----------------");

HttpUtil.fileDownload(req, res, null);

}else if(command.equals("/board/fileRemove.do")) {

System.out.println("remove");

System.out.println("----------------");

    HttpUtil.removeFile(req, res);

}

}

}



파일명: FrontController.java


[첨부(Attachments)]

FrontController-updated.zip





3. 변경 - Controller (BoardInsertMultiResultController.java)


변경 내용은 BoardInsertMultiResultController에 구현된 업로드 관련 소스를 HttpUtil로 이동시켰다.

파일명이 다소 너무 길어서 한 가지 이야기하면, 작명도 조금 연습하면 좋을 듯 싶다. (너무 긴 건 좋은 건 아니다.)


package com.fileWeb.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import com.fileWeb.util.HttpUtil;


public class BoardInsertMultiResultController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

HttpUtil.uploadFile(req, res);

HttpUtil.forward(req, res, "/WEB-INF/view/board/insertResult.jsp");

}

}


파일명: BoardInsertMultiResultController.java


[첨부(Attachments)]

BoardInsertMultiResultController-updated.zip






4. 변경 - Util 패키지로 변경 (HttpUtil.java)


다소 코드에 변화가 있었다.


변경 전: com.fileWeb.controller.HttpUtil.java

변경 후: com.fileWeb.util.HttpUtil.java




package com.fileWeb.util;


import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;


import javax.servlet.RequestDispatcher;

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;



public class HttpUtil extends HttpServlet {

private static final long serialVersionUID = 1L;

private static String charset = null; // 문자열


private static int SIZETHRESHOLD = 4096;

private static String UPLOAD_FOLDER = "upload";

private static String UPLOAD_TMP_FOLDER = File.separator + "WEB-INF" + File.separator + "temp";

private static long MAX_UPLOAD_SIZE = 3 * 1024 * 1024;

private static Map<String, Object> reqMap = null; // req 정보(MultiRequest)

private static Map<Integer, Map<String, Object>> fileMap = null; // 다중 파일 지원

private static int num = 0;


public static void forward(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {

try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);

}catch(Exception e) {

e.printStackTrace();

}

}

public static void uploadFile(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {


fileMap = new HashMap<Integer, Map<String, Object>>(); 

reqMap = new HashMap<>();

num = 1;

        

        PrintWriter out = res.getWriter();

        out.println("<HTML><HEAD><TITLE>Multipart Test</TITLE></HEAD><BODY>");

try {

            

            //디스크상의 프로젝트 실제 경로얻기

            //String contextRootPath = "c:" + File.separator + "upload";

String dirName = UPLOAD_FOLDER ; 

// String dirName = "upload"; 

String contextRootPath = req.getSession().getServletContext().getRealPath("/") + dirName;

            System.out.println("실제경로:" + contextRootPath);

            

            //1. 메모리나 파일로 업로드 파일 보관하는 FileItem의 Factory 설정

            DiskFileItemFactory diskFactory = new DiskFileItemFactory(); // 디스크 파일 아이템 공장

            diskFactory.setSizeThreshold(SIZETHRESHOLD); // 업로드시 사용할 임시 메모리

            // diskFactory.setSizeThreshold(4096); // 업로드시 사용할 임시 메모리

            diskFactory.setRepository(new File(contextRootPath + UPLOAD_TMP_FOLDER)); // 임시저장폴더

            // diskFactory.setRepository(new File(contextRootPath + UPLOAD_TMP_FOLDER)); // 임시저장폴더

            

            //2. 업로드 요청을 처리하는 ServletFileUpload생성

            ServletFileUpload upload = new ServletFileUpload(diskFactory);


            // upload.setSizeMax(3 * 1024 * 1024); //3MB : 전체 최대 업로드 파일 크기

            upload.setSizeMax(MAX_UPLOAD_SIZE); // 전체 최대 업로드 파일 크기

            

            //3. 업로드 요청파싱해서 FileItem 목록구함​​

            List<FileItem> items = upload.parseRequest(req); 


            Iterator<FileItem> iter = items.iterator(); //반복자(Iterator)로 받기​            

            while(iter.hasNext()) { //반목문으로 처리​    

                FileItem item = (FileItem) iter.next(); //아이템 얻기

                 //4. FileItem이 폼 입력 항목인지 여부에 따라 알맞은 처리

                

                if(item.isFormField()){ 

                //파일이 아닌경우

                    processFormField(out, item);

                    

                } else {

                //파일인 경우

                System.out.println("오류:" + item.getName());

               

                // 버그 개선 item 이름값 비어있을 때

                if ( item.getName() != "") {

                processUploadFile(out, item, contextRootPath);

                }

                System.out.println("오류2:");

                }

            }

            

        } catch(Exception e) {

            out.println("<PRE>");

            e.printStackTrace(out);

            out.println("</PRE>");

        }

out.println( "usrID(Map): " + reqMap.get("usrID") );

out.println( "usrPasswd(Map):" + reqMap.get("usrPasswd") );

        

        out.println("</BODY></HTML>");

// req.setAttribute("usrID", reqMap.get("usrID"));

// req.setAttribute("login", 1);//Object Type으로 넘어감

        req.setAttribute("reqMap", reqMap);

        req.setAttribute("fileMap", fileMap);

        

        // 방법3

        for( Integer key : fileMap.keySet() ){


            Map<String, Object> fileMapNode = fileMap.get(key);

            System.out.println( String.format("키 : %s, 값: %s", key, fileMapNode.get("fileName") ));

            

        }

        

// System.out.println("오류3:" + reqMap.get("usrID"));

}


//업로드한 정보가 파일인경우 처리

private static void processUploadFile(PrintWriter out, FileItem item, String contextRootPath)

throws Exception {

Map<String, Object> fileNode = new HashMap<String, Object>();


String dirName = UPLOAD_FOLDER ; 

String name = item.getFieldName(); // 파일의 필드 이름 얻기

String fileName = item.getName(); // 파일명 얻기

// 임시 - 실제 원본 이름 추출

File originalFile = new File(fileName);

String originalFileName = originalFile.getName();

System.out.println("임시:" + originalFileName );

String contentType = item.getContentType(); // 컨텐츠 타입 얻기

long fileSize = item.getSize(); // 파일의 크기 얻기

// 업로드 파일명을 현재시간으로 변경후 저장

String fileExt = fileName.substring(fileName.lastIndexOf("."));

String uploadedFileName = System.currentTimeMillis() + ""; 

System.out.println(fileExt);

System.out.println(uploadedFileName);

// 저장할 절대 경로로 파일 객체 생성

String realUploadFile = File.separator + dirName + File.separator + uploadedFileName;

System.out.println("실제 저장직전폴더:" + contextRootPath + realUploadFile);

File uploadedFile = new File(contextRootPath + realUploadFile);

item.write(uploadedFile); //파일 저장

//========== 뷰단에 출력 =========//

out.println("<P>");

out.println("파라미터 이름:" + name + "<BR>");

out.println("파일 이름:" + fileName + "<BR>");

out.println("콘텐츠 타입:" + contentType + "<BR>");

out.println("파일 사이즈:" + fileSize + "<BR>");

//확장자가 이미지인겨우 이미지 출력

if(".jpg.jpeg.bmp.png.gif".contains(fileExt.toLowerCase())) {

out.println("<IMG SRC='upload/" 

+ uploadedFileName 

+ "' width='300'><BR>");

}

out.println("</P>");

out.println("<HR>");

out.println("실제저장경로 : "+uploadedFile.getPath()+"<BR>");

out.println("<HR>");

// 파일 정보

fileNode.put("name", name);

fileNode.put("fileName", originalFileName);

fileNode.put("contentType", contentType);

fileNode.put("fileSize", fileSize);

fileNode.put("fileExt", fileExt);

fileNode.put("uploadedFileName", uploadedFileName);

fileNode.put("realName", uploadedFile.getName());

fileNode.put("realPath", uploadedFile.getPath());

fileMap.put(num, fileNode);

num++;

}

private static void processFormField(PrintWriter out, FileItem item) 

throws Exception{

String name = item.getFieldName(); //필드명 얻기

Object value = item.getString("UTF-8"); //UTF-8형식으로 필드에 대한 값읽기

// out.println(name + ":" + value + "<BR>"); //출력

reqMap.put(name, value);

}

/*

* 다운로드(Download)

*/

public static void fileDownload(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {


charset = (String) req.getAttribute("charset");

System.out.println(charset);

PrintWriter out = res.getWriter();

// 파일 업로드된 경로

String root = req.getSession().getServletContext().getRealPath("/");

String savePath = root + UPLOAD_FOLDER + File.separator + UPLOAD_FOLDER ;

// String savePath = root + "upload" + File.separator + "upload";

// 서버에 실제 저장된 파일명

String filename = "1601561525229" ;

System.out.println("파일 실제 폴더경로:" + savePath);

System.out.println("실제 파일명:" + filename);

// 실제 내보낼 파일명

String orgfilename = "license한글.txt" ;

req.setCharacterEncoding(charset);

res.setCharacterEncoding(charset);

InputStream in = null;

OutputStream os = null;

File file = null;

boolean skip = false;

String client = "";

try{

    // 파일을 읽어 스트림에 담기

    try{

        file = new File(savePath, filename);

        in = new FileInputStream(file);

    }catch(FileNotFoundException fe){

        skip = true;

    }

    client = req.getHeader("User-Agent");

    // 파일 다운로드 헤더 지정

    res.reset() ;

    res.setContentType("application/octet-stream");

    res.setHeader("Content-Description", "JSP Generated Data");

    if(!skip){

        // IE

        if(client.indexOf("MSIE") != -1){

            res.setHeader ("Content-Disposition", "attachment; filename="+new String(orgfilename.getBytes("KSC5601"),"ISO8859_1"));

        }else{

            // 한글 파일명 처리

            orgfilename = new String(orgfilename.getBytes("KSC5601"),"iso-8859-1");

            res.setHeader("Content-Disposition", "attachment; filename=\"" + orgfilename + "\"");

            res.setHeader("Content-Type", "application/octet-stream; charset=utf-8");

        }  

        res.setHeader ("Content-Length", ""+file.length() );

        os = res.getOutputStream();

        

        byte b[] = new byte[(int)file.length()];

        int leng = 0;

        while( (leng = in.read(b)) > 0 ){

            os.write(b,0,leng);

        }

    }else{

    // 한글 깨짐 - 해결

    res.setContentType("text/html;charset=" + charset);

        out.println("<html><head>");

        out.println("<script language='javascript'>alert('파일을 찾을 수 없습니다.');history.back();</script>");

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

    }

    in.close();

    os.close();

}catch(Exception e){

e.printStackTrace();

}

    

}


/*

*  파일 삭제, 폴더 삭제

*/

public static void removeFile(HttpServletRequest req, HttpServletResponse res) 

throws ServletException, IOException{


// 파일 업로드된 경로

String root = req.getSession().getServletContext().getRealPath("/");

String savePath = root + UPLOAD_FOLDER + File.separator + UPLOAD_FOLDER ;

String filename = "1601561525229" ;


        File file = new File(savePath, filename);

        // 파일이 존재할 떄

        if (file.exists()) {

        file.delete();

        }

        

        removeDirectory(req, res, savePath);

}

/*

*/

private static boolean removeDirectory(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {

boolean result = false;

File usrDir = new File(path);

if(!usrDir.exists()) {                  // 경로 존재 여부

            result = false;     

        }

else {

        File[] lowFiles = usrDir.listFiles();      // 경로 내의 파일 리스트

        

        // 폴더 삭제

        if ( usrDir.isDirectory() 

        && lowFiles.length == 0 ) {

   

        System.out.println("폴더 삭제처리 완료");

        usrDir.delete();

       

        return true;

       

    }else{

    result = false;

    }

        

        }

        

        return result;

}


}



파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil-updated.zip




* 맺음글(Conclusion)


파일 업로드, 다운로드, 삭제에 대해서 살펴보았다.

반응형

+ Recent posts