docker를 사용하는 이유

 

 

 

 

 

 

쿠버네티스란 무엇인가?

쿠버네티스는 컨테이너화된 워크로드와 서비스를 관리하기 위한 이식할 수 있고, 확장 가능한 오픈소스 플랫폼으로, 선언적 구성과 자동화를 모두 지원한다. 쿠버네티스는 크고 빠르게 성장하

kubernetes.io

 

 

 

 

 

 

 

 

소프트웨어 운영 플랫폼의 변화


 

 

 

  • Tranditional Deployment
  • 초기 조직은 애플리케이션을 물리 서버에서 실행했었다. 한 물리 서버에서 여러 애플리케이션의 리소스 한계를 정의할 방법이 없었기에, 리소스 할당의 문제가 발생했다. 예를 들어 물리 서버 하나에서 여러 애플리케이션을 실행하면, 리소스 전부를 차지하는 애플리케이션 인스턴스가 있을 수 있고, 결과적으로는 다른 애플리케이션의 성능이 저하될 수 있었다. 이에 대한 해결책은 서로 다른 여러 물리 서버에서 각 애플리케이션을 실행하는 것이 있다. 그러나 이는 리소스가 충분히 활용되지 않는다는 점에서 확장 가능하지 않았으므로, 물리 서버를 많이 유지하기 위해서 조직에게 많은 비용이 들었다.
  • Virtualized Deployment가상화를 사용하면 물리 서버에서 리소스를 보다 효율적으로 활용할 수 있으며, 쉽게 애플리케이션을 추가하거나 업데이트할 수 있고 하드웨어 비용을 절감할 수 있어 더 나은 확장성을 제공한다. 가상화를 통해 일련의 물리 리소스를 폐기 가능한(disposable) 가상 머신으로 구성된 클러스터로 만들 수 있다.
  • 각 VM은 가상화된 하드웨어 상에서 자체 운영체제를 포함한 모든 구성 요소를 실행하는 하나의 완전한 머신이다.
  • 그 해결책으로 가상화가 도입되었다. 이는 단일 물리 서버의 CPU에서 여러 가상 시스템 (VM)을 실행할 수 있게 한다. 가상화를 사용하면 VM간에 애플리케이션을 격리하고 애플리케이션의 정보를 다른 애플리케이션에서 자유롭게 액세스 할 수 없으므로, 일정 수준의 보안성을 제공할 수 있다.
  • Container Deployment
  • 컨테이너는 VM과 유사하지만 격리 속성을 완화하여 애플리케이션 간에 운영체제(OS)를 공유한다. 그러므로 컨테이너는 가볍다고 여겨진다. VM과 마찬가지로 컨테이너에는 자체 파일 시스템, CPU 점유율, 메모리, 프로세스 공간 등이 있다. 기본 인프라와의 종속성을 끊었기 때문에, 클라우드나 OS 배포본에 모두 이식할 수 있다.

 

 

 

 

 

 

 

가상화


  • 가상화 기술
  • 종속 된 컴퓨터 리소스를 추상화하여 서버, 스토리지, 네트워크 등의 소프트웨어 IT 서비스를 생성하는 솔루션
  • 하이퍼바이저 가상화
  • 하드웨어 위에 가상화 전문 소프트웨어인 하이퍼바이저를 설치하고 가상 환경을 제어하는 것
  • 호스트 가상화호스트 OS 상의 오버헤드가 크기 때문에 낮은 사양의 PC에서는 선호되지 않음
오버헤드: 가상화를 위해 필요한 추가 CPU 리소스, 디스크 용량, 메모리 사용량

 

  • 가상 환경을 만들기 위해 운영 중인 하드웨어(호스트)에 가상화 소프트웨어를 설치해 게스트 OS를 운영하는 것
  • 컨테이너 가상화호스트 OS의 자원을 컨테이너들끼리 공유하며 각각 필요한 자원을 할당 받아 실행 되기 때문에 오버헤드가 적음
  • 하이퍼 바이저와 게스트 OS가 필요하지 않아 용량이 작고, 운영체제 부팅이 필요하지 않아 서비스 시작 시간도 짧음
  • 호스트 OS 상에서 논리적으로 구역을 나눠 애플리케이션 동작을 위한 라이브러리와 애플리케이션 등을 그 안에 넣고 사용하는 것

 

 

 

 

 

Docker란?


  • 애플리케이션을 개발, 배포, 실행하기 위한 플랫폼
  • 컨테이너를 사용하여 애플리케이션 및 지원 구성 요소를 개발
  • 리눅스 자체 기능을 사용하여 프로세스 단위의 격리 환경을 제공
  • 배포 용량과 시간을 단축하고, 성능 손실을 최소화 시킴

 

 

 

 

 

Docker의 장점


  • 도커 엔진 아래에는 물리적인 컴퓨터 호스트가 있으며 도커 엔진 위에 각각의 컨테이너가 개별적인 리소스를 할당 받아서 마치 하나의 단독 컴퓨터처럼 동작함
  • 전통적인 배포 방식은 개발 후 컴퓨터에 OS를 설치하고 런타임 환경을 구성 한 뒤 배포 파일을 넣고 실행하는 것인데 이 과정을 한번에 처리할 수 있는 스크립트(이미지라고 부름)를 도커를 통해 손쉽게 만들 수 있음
  • 이미지는 원격 저장소에서 도커 엔진을 사용해 내려 받을 수 있고 내려 받은 이미지로 어떤 호스트 PC건 동일하게 컨테이너 생성이 가능함
  • 즉, 컴퓨터 초기 세팅이 없는 상태에서 물리적인 처리 없이 논리적으로 run 만 하면 순식간에 환경을 설정할 수 있음
  • 확장 편리
→ 서버의 부하로 인해 분산이 필요하여 서버의 수를 늘려야 할 경우 컴퓨터를 더 사서 런타임 환경을 구축하고 배포 파일을 실행하는 처리 또는 해당 서버가 불필요해졌을 때 낭비되는 상황 없이 유연하게 컨테이너를 올렸다 내렸다 하면서 리소스를 조절하며 확장 할 수 있음

 

 

 

 

 

 

Docker Swarm, Kubernetes


  • 오케스트레이션 툴
  • 컨테이너화된 애플리케이션의 자동 디플로이, 스케일링 등을 제공하는 관리 시스템
→ 오토스케일링 : 리소스의 임계치를 설정하여 컨테이너 상태 및 리소스 메트릭 정보를 모니터링 해서 특정 이벤트에 대해 컨테이너를 확장 및 축소하는 기능
→ 리소스 : CPU, 메모리, WAS의 Thread 등등

 

 

 

 

 

 

 

 

 

 

'Programming > DOCKER' 카테고리의 다른 글

도커컨테이너관리하기  (0) 2022.05.03
도커 이미지 관리하기  (0) 2022.05.03
도커의 구조와 특징  (0) 2022.05.02
도커 (docker) 설치하기  (0) 2022.05.02
STS에서 Maven build하기  (0) 2022.05.02

 

 

 

deploy

 

 

 

 

 

 

배포(Deploy)


  • 개발자가 로컬 PC에서 개발하고 테스트 진행 후 문제가 없으면 실서버에 반영하는 것

 

 

 

 

빌드(Build)


  • 소스 코드 파일을 컴퓨터에서 실행할 수 있는 독립적인 형태로 변환하는 과정과 그 결과
  • 컴파일 된 코드를 실제 실행할 수 있는 상태로 만드는 일
  • 개발한 어플리케이션을 실행 가능한 배포 파일 형태(jar,war)로 변환하는 것

 

 

 

 

빌드 도구(Build Tool)


  • 소스 코드에서 실행 가능한 애플리케이션 생성을 자동화하는 프로그램
  • Ex. Maven, Gradle

 

 

 

 

 

 

 

 

 

STS에서 Maven build 하는 방법 


빌드에 앞서 해당프로젝트에 오류가 없는지 확인 한다. 문제가 없다면

 

프로젝트 우클릭 > Run As 클릭 > 5. Maven build 클릭

 

 

 

 

 

 

 

 

Gols(목표) 입력 >  Run 클릭 

 

 

 

 

 

이 과정을 통해 콘솔창에서 확인 해야하는 것 : Build Success

 

 

 

 

 

Build Success를 확인 > 프로젝트 내 target 폴더 우클릭 > Refresh

 

 

 

 

새로고침하면 다음과 같은 SNAPSHOT.jar 파일을 확인할 수 있다. 

단독으로 배포, 단독 실행이 가능한 .jar 파일이 생성된다. 

 

 

 

 

 

 

 

 

 

 

 

 

deploy 테스트 


 

 

  1. 위의 과정을 통해 생성된 SNAPSHOT.jar 파일을 실행을 원하는 폴더로 따로 옮겨준다. 
  2. 테스트를 위해 C: 내에 deploytest 폴더를 따로 만들어 옮겨 주었다. 
  3. 폴더 내에서 cmd를 검색한다. 

 

 

 

 

 

 

  • cmd 내 하얗게 드래그 쳐져있는 부분과같이 입력한다. 
java -jar crud-0.0.1-SNAPSHOT.jar

 

 

 

 

 

 

동작에 문제가 없다면 다음과같은 화면이 뜨면서 나의 포트번호 (8001)도 함께 확인할 수 있다. 

 

 

 

 

 

배포한 프로그램을 실행 후 종료하는 법
  • ctrl + c 누름
  • 종료가 되었다면 다음과같은 화면이 뜬다. 

 

 

 

 

 

 

백그라운드에서 역시 잘 동작됨을 확인한다. 

동작중인 포트를 종료하는 방법으로는 포트를 킬해주는 방법이 있다. 

cmd 창에 jsp를 입력해주면 스냅샷 파일앞에 다섯자리 번호가 뜬다.

포트번호는 저마다 다르다.

 

cmd > jsp 입력 

 

 

 

 

 

 

차례로 cmd 창에 [taskkill -f /PID 검색해서 나온 포트번호]를 입력해 종료가 된 모습을 확인할 수 있다.  

taskkill -f /PID 번호다섯자리

 

 

 

 

 

 

 

 

 

 

build한 .jar 파일 바로가기 만들기 


매번 위와같은 과정을 거치는게 번거롭다면 바로가기를 만들어서 쉽게 실행할 수 있다. 

 

 

 

 

바탕화면에 바로가기를 만든다. 

새로만들기 > 바로가기 

 

 

 

 

 

  • 바로가기의 항목 위치를 입력해야하는데 위에서 옮겨둔 SNAPSHOT.jar 파일의 폴더명과 파일명을 정확히 입력해준다.
  • 나의 디바이스 내 폴더명에 맞게 작성했으니 작성자마자 아래 빨간줄(폴더경로)을 제외한 입력란은 상이할 것이다. 
  • 오타에 유의하면서 항목위치 입력란에 다음과같이 작성해준다. 

 

C:\Windows\System32\cmd.exe /k "javaw -jar C:\dev\deploytest\crud-0.0.1-SNAPSHOT.jar"

 

 

 

 

 

바로가기명은 임의로 작성해준다. 

 

 

 

바로가기 생성 시 바탕화면에서 다음과같은 아이콘을 확인할 수 있다. 

 

 

 

 

이때 주소창에 다음 포트번호를 치고 들어가면 브라우저에서 아래와 같은 화면을 통해 잘 구동하고있음을 확인할 수 있다. 

 

 

 

 

 

 

 

 

 

 

 

 

윈도우 시작과 동시에 동작하게 하기


 

 

  • 윈도우 + r 을 눌러 실행창을 실행한다. 
윈도우키 + R

 

 

 

 

 

  • 엔터를 치면 뜨는 시작프로그램 파일에 앞서 만든 바로가기 파일을 담아둔다.
  • pc가 켜지면 항상 이 프로세스가 동작하게 된다. 
  • 그러나 이 방법은 이미 프로그램이 동작중이라는 이유로 테스트중에 오류를 야기시킬 가능성이 많으므로 꼭 필요한 경우가 아니라면 지양하려 한다. 

 

 

 

 

 

 

 

(+) 

 

만약 앞서 설정한 포트 번호 8001을 바꾸고 싶다면

 

프로젝트 우클릭 > Run As를 통해 들어간 경로에서 chap01을 삭제하면 된다. 

 

 

 

 

 

 

지금까지의 테스트는 Windows OS에서의 테스트로 GUI 환경이었지만 일반적인 배포 운영 환경은 Linux로 CLI 환경이다.

 

앞서 배포한 파일은 기본적으로는 외부에서 접근이 불가능하다. 

공유가 할당해주는 내부 아이피를 사용하고있기 때문인데 외부에서 내부아이피로 접속요청을 하고자 한다면 누구에게 할당할 것인지를 설정 해주어야 외부 접속이 가능하다. 

 

 

 

 

 

 

 

 

 

 

 

 

포트 포워딩


포트 포워딩을 해주면 실행시킨 jar 파일을 외부에서도 접속하도록 할 수 있다. 

 

 

 

 

 

 

 

  • 아이피를 기억해서 요청하기 힘들다면 도메인을 사용할 수 있다.
  • iptime에서 제공하는 DDNS 설정으로 도메인을 만들거나 무료 도메인을 발급 받을 수도 있으나 매번 쳐야하는 주소가 아이피 만큼이나 복잡하다는 단점이 있다.
  • 단순명료한 도메인이 필요하다면 유료로 도메인 등록 대행 기관을 통해 도메인을 발급 받아 관리할 수 있다.

 

https://domain.gabia.com/

 

가비아: 대한민국 도메인 점유율 1위

대한민국 100만 도메인 등록 업체

domain.gabia.com

 

 

 

 

예시로 아무 주소나 쳐 보았다. 주소에 따라 가격대별로 상이한 모습니다. 

 

 

 

 

 

 

 

 

 

AWS(Amarzon Web Service)


개개인의 pc를 서버로서 물리적으로 사용할 수는 있으나 이 방법은 해당 pc가 꺼지는 순간 서비스가 종료된다.

때문에 하드웨어의 제약 없이 애플리케이션을 배포하기 위해 클라우드 환경을 활용하는 경우가 많다.

 

 

클라우드 환경의 종류는 다음과 같은 것들이 있다. 

  • EC2 : 서버용 컴퓨터 제공
  • RDS : 데이터베이스 서버 제공
  • Route53 : 도메인 주소 제공

 

다만 이 방법은 만드는 것은 무료이지만 접속량에 대해 과금이 발생할 수 있다는 점에 유의한다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Programming > DOCKER' 카테고리의 다른 글

도커컨테이너관리하기  (0) 2022.05.03
도커 이미지 관리하기  (0) 2022.05.03
도커의 구조와 특징  (0) 2022.05.02
도커 (docker) 설치하기  (0) 2022.05.02
docker를 사용하는 이유  (0) 2022.05.02

 

 

 

 

 

 

폴더 구조는 다음과 같다. 

 

 

 

 

간단한 확인을 위해 list와 order의 html을 작성한다. 

 

 

 

 

list.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>menu list</title>
</head>
<body>
	<h1 align="center">메뉴 리스트</h1>
</body>
</html>

 

 

order.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>order</title>
</head>
<body>
	<h1 align="center">주문 페이지입니다.</h1>
</body>
</html>

 

 

 

 

 

 

 

menu와 order의 Controller를 작성한다. 

 

 

 

 

MenuController

package com.greedy.security.menu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/menu")
public class MenuController {
	
	@GetMapping("/list")
	public String findMenuList() {
		
		return "menu/list";
	}
	
}

 

 

 

OrderController

package com.greedy.security.order.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class OrderController {
	
	@GetMapping("/order")
	public String orderPage() {
		
		return "order/order";
	}
}

 

 

 

 

 

 

 

 

 


잘못된 로그인 정보를 입력할시 보여질 화면도 구현한다. 

 

 

 

 

 

 

denied.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>denied</title>
</head>
<body>
	<h1 align="center">접근 권한이 없습니다. 관리자에게 문의해주세요.</h1>
</body>
</html>

 

 

 

 

 

 

 

 

실행


 

 

  • 실행 시 다음과같은 화면이 뜬다. 

 

 

  • 로그인 페이지는 다음과 같다. 아이디와 비밀번호를 입력한다. 

 

 

  • [000님 환영합니다] 라는 문구가 뜨도록 했는데 세가지 authentication 에 따라 다르게 뜨는 것을 확인 할 수 있다. 

 

 

 

 


principal


로그인 된 User 객체의 정보를 담고 있다.

 


principal.username


로그인 시 입력한 id 값




 

<h3><span sec:authentication="principal"></span>님 환영합니다</h3>

 

<h3><span sec:authentication="principal.username"></span>님 환영합니다</h3>

 

<h3><span sec:authentication="principal.name"></span>님 환영합니다</h3>

 

 

 

 

  • 관리자로 로그인 시에도 다음과같이 바뀌는 것을 확인할 수 있다. 
  • 즉, 회원 권한에 따라 로그인시 나타나는 화면이 달라진다. 

 

 

 

 

  • 로그아웃 시 다시 로그인이 가능해지며 메인화면으로 돌아간다. 

 

 

 

 

 

 

 

 

'Programming > Spring Framework' 카테고리의 다른 글

Spring-Boot-Crud (2) : Thymeleaf  (0) 2022.05.09
Spring-Boot-Crud (1)  (0) 2022.05.09
Spring Security (1)  (0) 2022.04.25
Spring Boot 초기 설정  (0) 2022.04.20
Spring Boot 초기 환경설정  (0) 2022.04.20

 

 

 

 

 

Spring Security (1)

 

 

 


 

새로운 DB를 만들어서 읽혀준다. 권한과 관련된 부분을 테스트 할 것이다. 

1번 권한을 가진 사람을 AUTHORITY_CODE 1을, 2번 권한을 가진 사람은  AUTHORITY_CODE 1, 2 코드를 모두 가지고 있다. 

 

 

 

 

 

 

 

  • 스프링 스타터 프로젝트로 새 프로젝트를 만들어 준다. 

 

 

추가한 의존성은 다음과 같다. 

 

 

 

 

 

 

프로젝트 전체 구조는 다음과 같다. 


 

 

 

 

 

 

 

 

 

 

 

 

Application.yml

오라클DB를 정보를 기입하여 연결한다. 

# server port config
server :
  port : 8001

# datasource config
spring :
  datasource : 
    driver-class-name : oracle.jdbc.driver.OracleDriver
    url : jdbc:oracle:thin:@localhost:1521:xe
    username :
    password :

# mybatis config
mybatis :
  mapper-locations : mappers/**/*.xml

 

 

 

 

 

 


  • 테스트를 위한 간단한 메인과 로그인 화면을 구현한다. 

 

 

 

main.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>
	<h1 align="center">Spring Security Project에 오신 것을 환영합니다.</h1>
	
	<div align="right">
		<!-- isAuthenticated() : 인증(로그인) 되어 있는지 확인 -->
		<th:block sec:authorize="isAuthenticated()">
			<!-- 
				principal : 로그인 된 User 객체의 정보를 담고 있다.
				principal.username : 로그인 시 입력한 id 값
				그 외의 정보가 필요할 경우 User 타입을 상속한 클래스를 
                만들어서 커스터마이징 할 수 있다.
			 -->
			<h3><span sec:authentication="principal"></span>님 환영합니다</h3>
			<h3><span sec:authentication="principal.username"></span>님 환영합니다</h3>
			<h3><span sec:authentication="principal.name"></span>님 환영합니다</h3>
			<button onclick="location.href='/member/logout'">로그아웃</button>
		</th:block>
		<!-- isAnonymous() : 인증(로그인) 되어 있지 않은 경우 -->
		<th:block sec:authorize="isAnonymous()">
			<h3>로그인이 필요한 서비스입니다.</h3>
			<button onclick="location.href='/member/login'">로그인</button>
			<button>회원가입</button>
		</th:block>
	</div>
	
	<button onclick="location.href='/menu/list'">메뉴 보기</button>
	<th:block sec:authorize="hasRole('MEMBER')">
		<button onclick="location.href='/order'">주문하기</button>
	</th:block>
	<th:block sec:authorize="hasRole('ADMIN')">
		<button onclick="location.href='/admin/dashboard'">관리자메뉴</button>
	</th:block>
	
</body>
</html>

 

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login page</title>
</head>
<body>


	<!-- 
		spring security의 formlogin을 할 경우 username, password로 꺼내게 되어 있으므로
        name 속성 설정에 유의한다.
		"/member/login" 요청은 시큐리티에 설정해두었으므로 해당 요청을 가로채서
		userDetailsService의 용도로 memberService를 사용하겠다고 전달 했기 
        때문에 loadUserbyUserName 메소드가 호출 되어
		User 객체를 리턴하게 되며 비밀번호 확인(설정 된 passwordEncoder 사용) 
        후 인증 되면 세션에 저장하는 기능이 자동으로 동작한다.
	 -->
     
     
	<h1 align="center">로그인 페이지</h1>
	<div align="center">
		<form action="/member/login" method="post">
			<div>
				<p style="color:red;">[[${errorMessage}]]</p>
			</div>
			<div>
				<span>아이디 : </span>
				<input type="text" name="username">
			</div>
			<div>
				<span>비밀번호 : </span>
				<input type="password" name="password">
			</div>
			<div>
				<button>로그인</button>
			</div>
		</form>
	</div>

</body>
</html>

 

 

 

 

 

 

 

config 폴더 내 클래스를 작성한다. 

 

 

 

 

Chap04SecuritySessionLoginApplication

package com.greedy.security.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Chap04SecuritySessionLoginApplication {

	public static void main(String[] args) {
		SpringApplication.run(Chap04SecuritySessionLoginApplication.class, args);
	}

}

 

 

SpringSecurityConfiguration

package com.greedy.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.greedy.security.handler.LoginFailHandler;
import com.greedy.security.member.model.service.MemberService;

/* 스프링 시큐리티 설정 활성화 + bean 등록 가능 */
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
	
	private final MemberService memberService;
	private final PasswordEncoder passwordEncoder;
	
	@Autowired
	public SpringSecurityConfiguration(MemberService memberService,
    								PasswordEncoder passwordEncoder) {
		this.memberService = memberService;
		this.passwordEncoder = passwordEncoder;
	}

	
	/* 암호화에 사용할 객체 BCryptPasswordEncoder bean 등록 - ContextConfiguration */

	/* 정적 리소스는 권한이 없어도 요청 가능하게 무시할 경로를 작성한다 */
	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/css/**", "/js/**", "/images/**");
	}

	/* HTTP 요청에 대한 설정 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.csrf().disable()	/* csrf는 기본적으로 활성화 되어 있으므로 비활성화 처리 */
			/* 요청에 대한 권한 체크 */
			.authorizeHttpRequests()
				/* 요청 보안 수준의 세부적인 설정 */
				/* "/menu/**" 요청은 인증(로그인) 되어야 함을 명시 */
				.antMatchers("/menu/**").authenticated()
				/* "/menu/**" 의 GET 요청은 member 에게 허용 */
				.antMatchers(HttpMethod.GET, "/menu/**").hasRole("MEMBER")
				/* "/menu/**" 의 POST 요청은 admin 에게 허용 */
				/* hasRole 앞에 ROLE_가 자동으로 붙음 */
				.antMatchers(HttpMethod.POST, "/menu/**").hasRole("ADMIN")
				/* "/admin/**" 의 요청은 admin 에게 허용 */
				.antMatchers("/admin/**").hasRole("ADMIN")
				/* 그 외의 모든 요청은 허가함 - 인증(로그인) 되지 않은 사용자도 요청 가능 */
				.anyRequest().permitAll()
			.and()
				/* 로그인 설정 */
				.formLogin()
				/* 로그인 페이지 설정 */
				.loginPage("/member/login")
				/* 성공 시 랜딩 페이지 설정 */
				.successForwardUrl("/")
				/* 로그인 실패 시의 핸들러 설정 */
				.failureHandler(loginFailHandler())
			.and()
				/* 로그아웃 설정 */
				.logout()
				/* 로그아웃 주소 */
				.logoutRequestMatcher(new AntPathRequestMatcher("/member/logout"))
				/* JSESSIONID 쿠키 삭제 */
				.deleteCookies("JSESSIONID")
				/* 세션 만료 */
				.invalidateHttpSession(true)
				/* 성공 시 랜딩 페이지 */
				.logoutSuccessUrl("/")
			.and()
				/* 인증/인가 예외 처리 */
				.exceptionHandling()
				/* 인가 되지 않았을 때 - 권한이 없을 때 이동할 페이지 */
				.accessDeniedPage("/common/denied");
	}
    
    

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
		/* 로그인, 로그아웃은 MemberController에 작성하지 않고 
        스프링 시큐리티 모듈을 통해 처리 */
		/* 사용자 인증을 위해서 사용할 MemberService 등록, 
        사용하는 비밀번호 인코딩 방식 설정 */
        
		auth.userDetailsService(memberService).passwordEncoder(passwordEncoder);
	}
    
    
	
	/* 로그인 실패 핸들러 bean 등록 */
	@Bean
	public LoginFailHandler loginFailHandler() {
		return new LoginFailHandler();
	}
}

 

 

 

MybatisConfiguration

package com.greedy.security.config;

import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan(basePackages = "com.greedy.security", annotationClass = Mapper.class)
public class MybatisConfiguration {

}

 

 

 

 

 

 

 

main 폴더 내 MainController를 작성한다. 

 

 

 

MainController

package com.greedy.security.main.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class MainController {
	
	@GetMapping(value = {"/", "/main"})
	public String main() {
		return "main/main";
	}
	
	@PostMapping(value="/")
	public String redirectMain() {
		return "redirect:/";
	}
	
}

 

 

 

 

 

 

 

 

회원에 관련돼 구현될 클래스를 작성한다.

 

 

 

 

MemberController

package com.greedy.security.member.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/member")
public class MemberController {

	@GetMapping("/login")
	public void loginForm() {}
	
	@PostMapping("/login")
	public void loginForm(@RequestParam(required=false) String errorMessage, Model model) {
		model.addAttribute("errorMessage", errorMessage);
	}
}

 

 

MemberMapper

package com.greedy.security.member.model.dao;

import org.apache.ibatis.annotations.Mapper;

import com.greedy.security.member.model.dto.MemberDTO;

@Mapper
public interface MemberMapper {

	MemberDTO findMemberById(String username);

}

 

 

 

AuthorityDTO

package com.greedy.security.member.model.dto;

import lombok.Data;

/* TBL_AUTHORITY */
@Data
public class AuthorityDTO {
	
	private int code;
	private String name;
	private String desc;
	
}

 

authorityDTO 내의 필드. 권한을 확인에는 code 와 name이 필요하도록 한다.  

 

 

 

MemberDTO

package com.greedy.security.member.model.dto;

import java.util.Date;
import java.util.List;

import lombok.Data;

/* TBL_MEMBER */
@Data
public class MemberDTO {
	
	private int no;									//회원번호
	private String id;								//회원아이디
	private String pwd;								//회원비밀번호
	private String tempPwdYn;						//임시비밀번호여부
	private Date pwdChangedDatetime;				//회원비밀번호변경일자
	private String pwdExpDate;						//회원비밀번호만료일자
	private String name;							//회원이름
	private Date registDatetime;					//회원가입일시
	private int accumLoginCount;					//누적로그인횟수
	private int loginFailedCount;					//로그인연속실패횟수
	private String accLockYn;						//계정잠금여부
	private String accInactiveYn;					//계정비활성화여부
	private String accExpDate;						//계정만료일자
	private String accExpYn;						//계정만료여부
	private Date accSecessionDatetime;				//계정탈퇴일시
	private String accSecessionYn;					//계정탈퇴여부
	
	/* TBL_MEMBER_ROLE - 한 멤버는 여러 권한을 가질 수 있다 */
	private List<MemberRoleDTO> memberRoleList;		//권한 목록
	
}

 

 

 

MemberRoleDTO

package com.greedy.security.member.model.dto;

import lombok.Data;

/* TBL_MEMBER_ROLE */
@Data
public class MemberRoleDTO {
	
	private int memberNo;
	private int authorityCode;
	
	/* TBL_AUTHORITY - 권한 코드별로 가지는 권한을 나타냄 */
	private AuthorityDTO authority;
}

 

 

 

 

UserImpl

package com.greedy.security.member.model.dto;

import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
public class UserImpl extends User {
	
	private int no;									//회원번호
	private String id;								//회원아이디
	private String pwd;								//회원비밀번호
	private String tempPwdYn;						//임시비밀번호여부
	private Date pwdChangedDatetime;				//회원비밀번호변경일자
	private String pwdExpDate;						//회원비밀번호만료일자
	private String name;							//회원이름
	private Date registDatetime;					//회원가입일시
	private int accumLoginCount;					//누적로그인횟수
	private int loginFailedCount;					//로그인연속실패횟수
	private String accLockYn;						//계정잠금여부
	private String accInactiveYn;					//계정비활성화여부
	private String accExpDate;						//계정만료일자
	private String accExpYn;						//계정만료여부
	private Date accSecessionDatetime;				//계정탈퇴일시
	private String accSecessionYn;					//계정탈퇴여부
	
	/* TBL_MEMBER_ROLE - 한 멤버는 여러 권한을 가질 수 있다 */
	private List<MemberRoleDTO> memberRoleList;		//권한 목록

	public UserImpl(String username, String password, 
    			Collection<? extends GrantedAuthority> authorities) {
		super(username, password, authorities);
	}
	
	public void setDetails(MemberDTO member) {
		this.no = member.getNo();
		this.id = member.getId();
		this.pwd = member.getPwd();
		this.tempPwdYn = member.getTempPwdYn();
		this.pwdChangedDatetime = member.getPwdChangedDatetime();
		this.pwdExpDate = member.getPwdExpDate();
		this.name = member.getName();
		this.registDatetime = member.getRegistDatetime();
		this.accumLoginCount = member.getAccumLoginCount();
		this.loginFailedCount = member.getLoginFailedCount();
		this.accLockYn = member.getAccLockYn();
		this.accExpDate = member.getAccExpDate();
		this.accSecessionDatetime = member.getAccSecessionDatetime();
		this.accSecessionYn = member.getAccSecessionYn();
		this.memberRoleList = member.getMemberRoleList();
	}

}

 

 

 

MemberService

package com.greedy.security.member.model.service;

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

/* 스프링 시큐리티에서 제공하는 기능을 이용해 로그인 로직을 작성해야 함 */
public interface MemberService extends UserDetailsService{

}

 

 

 

MemberServiceImpl

package com.greedy.security.member.model.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.greedy.security.member.model.dao.MemberMapper;
import com.greedy.security.member.model.dto.AuthorityDTO;
import com.greedy.security.member.model.dto.MemberDTO;
import com.greedy.security.member.model.dto.MemberRoleDTO;
import com.greedy.security.member.model.dto.UserImpl;

@Service
public class MemberServiceImpl implements MemberService {
	
	private MemberMapper memberMapper;
	
	@Autowired
	public MemberServiceImpl(MemberMapper memberMapper) {
		this.memberMapper = memberMapper;
	}

	/* 사용자 아이디를 통해 사용자 정보 조회하는 기능 - 로그인 시 호출 될 메소드 */
	@Override
	public UserDetails loadUserByUsername(String username) 
    								throws UsernameNotFoundException {
		
		/* 우리가 정의한 타입으로 유저 조회 */
		MemberDTO member = memberMapper.findMemberById(username);
		
		/* null 값이 없게 하기 위해 조회 된 값이 없을 시 빈 객체 */
		if(member == null) member = new MemberDTO();
		
		/* 권한 리스트 */
		List<GrantedAuthority> authorities = new ArrayList<>();
		
		if(member != null && member.getMemberRoleList() != null) {
			
			for(MemberRoleDTO role : member.getMemberRoleList()) {
				AuthorityDTO authority = role.getAuthority();
				
				if(authority != null) {
					authorities.add(new SimpleGrantedAuthority(authority.getName()));
				}
			}
		}
		
		// return new User(member.getId(), member.getPwd(), authorities);
	
    
		
		/* User 객체에 담기지 않는 추가정보를 User객체를 extends한 
        UserImpl에 담아서 리턴한다. */
        
		UserImpl user = new UserImpl(member.getId(), member.getPwd(), authorities);
		user.setDetails(member);
		
		return user;
	
	}

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Programming > Spring Framework' 카테고리의 다른 글

Spring-Boot-Crud (1)  (0) 2022.05.09
Spring Security (2)  (0) 2022.04.25
Spring Boot 초기 설정  (0) 2022.04.20
Spring Boot 초기 환경설정  (0) 2022.04.20
5. Spring AOP (2)  (0) 2022.04.11

 

 

 

 

 

 

 

 

박스안에 들어가있는 3개는 기존에 사용했던 것이고 db연동과 관련해서 새롭게 의존성을 추가해준다. 

 

오라클과 

 

마이바티스와

 

Lombok 을 추가한 후 finish를 눌러준다. 

 

 

 

 

pom.xml에서 좀 더 많은 의존성이 추가된 것을 확인 해 볼 수 있다. 

 

 

 

 

 

 

에러로그를 보면 url이없다고 나온다. 이는 데이터 베이스 의존성을 추가했기 때문이다. 

db 연결정보가 없기 때문이며 접속 정보를 추가해주어야한다. 

 

 

 

 

설정 정ㅂ보는 여기서 작성 

사용해야하는 키값은 다음문서에서 참조할 수 있다. 

 

 

 

 

 

설정 파일은 다음과같이 작성해준다. 패스워드만 가렸다. 

 

 

 

위와같은 파일은 알아보기 어렵기 때문에 좀더 편의성을위해 

파일을 만들어 yml파일을 생성해준다.

 

: 와 tab을 이용해 계층을 형성해주어가며 작성해준다. 

 

 

문제가 없다면 콘솔창에 다음과같이 뜬다. 

 

 

 

 

 

 

Lombok 라이브러리 추가 

 

패키지 익스플로러 > Maven Dependencies > lombok 을 찾는다. 해당파일의 내 컴퓨터 경로명을 찾아준다. 

 

내 로컬 저장소에서의 경로는 다음과 같았다. 

 

 

여기서 cmd 창을 불러오기위해 주소창에 cmd. 을 입력해준다. 

 

 

 

 

cmd 창에 다음과 같이 입력한다. 

java -jar lombok-1.18.22.jar

1.18.22은 버전이다. 

 

입력후엔 경고창이 뜨며 찾을 수 없다고 나오는데 이때 침착하게 select location창을 누른다. 

 

 

버전에 유의하며 선택해준다. 

단, 여기서 경로 중 한글폴더명 등 경로에 한글이 포함되면 오류가 발생한다. 

경로에 한글값이 포함돼있지않은지 한번 더 확인후 설치해주도록 한다. 

 

 

아래 창이 떴다면 정상적으로 설치된것이다. 

 

 

컴파일 에러가 사라진걸 확인할 수 있다. 

 

 

 

 

각각의 클래스 별로 수행해야할 로직은 다음과 같다. 

 

 

 

 

 

dtd 필요 

 

 

 

 

 

 

 

 

 

 

 

 

아까 만든 dao 파일을 네임스페이스로 지정해준다. 

 

 

 

 

yml 파일쪽에 매퍼를 등록시켜주어야 인식할 수 있다. 

 

 

 

 

 

같은 패키지 아래 있지않으면 동작시 실패한다. 

 

 

 

 

 

기본동작파일을 똑같이 config 아래에 넣어준뒤 패키지명을 수정해주고 동작시키면 오류가 발생하지않는다. 

 

정상적으로 실행되는 화면

 

 

 

결국 test를 하고싶다면 파일구조가 동일해야함을 알 수 있다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Programming > Spring Framework' 카테고리의 다른 글

Spring Security (2)  (0) 2022.04.25
Spring Security (1)  (0) 2022.04.25
Spring Boot 초기 환경설정  (0) 2022.04.20
5. Spring AOP (2)  (0) 2022.04.11
5. Spring AOP (1)  (0) 2022.04.11

 

 

 

 

 

Features

  • Create stand-alone Spring applications
  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
  • Provide opinionated 'starter' dependencies to simplify your build configuration
  • Automatically configure Spring and 3rd party libraries whenever possible
  • Provide production-ready features such as metrics, health checks, and externalized configuration
  • Absolutely no code generation and no requirement for XML configuration

 

 

스프링 프레임워크와 별도의 기능은 아님

독립형, 톰캣 내장, 스타터 제공, 라이브러리 유동성 관리(버전), 스프링 외 다른 회사의 라이브러리도 가능, XML 을 어노테이션 형식으로 적용

 

 

 

 

 

 

spring.io 홈페이지에 방문해 Project > Spring Tools

 

 

https://spring.io/

 

Spring makes Java simple.

Level up your Java code and explore what Spring can do for you.

spring.io

 

 

 

 

 

 

 

 

 

Maven 과 Gradle 둘 중 하나 선택가능. 결국 MVN repository에서 다음과같이 가져다 사용된다. 

 

 

 

 

 

 

 

 언어는 자바언어를 사용

 

 

SNAPSHOT 이나 M2 같은경우 아직 준비중이니 안정화가 떨어진다.

 

 

 

 현재쓰기 가장 적합한 버전은 2.6.6버전이다.

 

 

 참고로 부트는 2.6.6 을 스프링 버전은 5버전대를 사용했다. 

 

 

 

 Project Metadata 같은 경우는 다음과같이 설정했다. 

 

 

자바의 여러버전이있지만 8, 11, 17 버전이 지원이 오래동안 되는걸 확인할 수 있다. (LTS, Long Term Support)

즉 안정적으로 사용하기위해 앞선 세버전을 많이 사용한다.

 

 

 

 

 

ADD를 눌러 다음과같이 추가한다.  

 추가한 STARTER에 관한 설명

https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters

 

 

 

해당버튼을 누르면 압축 파일로 다운 받을 수 있다. 압축을 푼 후 작업환경에  임포트 시켜준다. 

 

 

 

 

 

처음엔 시간이 좀 걸린다.  

 

 

 

 

 파일구조를 간략히 보면 각각의 파일이 들어가야 할 곳은 이러하다. 

 

 

 

 

 Test 는 잘 적용 및 실행 되는지 테스트 해보기위한 도구이다. 

 

 

테스트임을 나타내기 위한 어노테이션이 붙는다. 

 

 

 

 

 

 

 

 

 

 

 

포트번호를 바꾸고 실행을 하면 다음과같이 404 오류 페이지가 뜬다. 

 

 

 

 기본적인 커스터마이징은 다음과같다. 포트번호 8080이 기본이지만 덮어쓰는 형식으로 다르게 적용할 수도 있다. 

 

https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties

 

 

순서대로 자바파일, 정적파일, 뷰 등 작성

 

 

 

 

 

프로젝트 만들기위해선 위의 3버전이 아닌 4버전으로 적용한다. 

tools 탭으로 가서 이클립스용 spring tools4를 다운받는다. 

 

https://spring.io/tools

 

 

 

 

 jar 파일이므로 압축은 따로 풀지않도록 주의한다. 

 

 

해당 파일의 경로안에서 cmd.를 입력하면 해당 파일의 경로로 동작시킬 수 있다. 

 

 

 

 

 

 

 

 

 

 

스타터 프로젝트를 열어 

 

 

 

 

 

 

 

 

html파일을 만들어보고싶으나 현재 상태에서는 기본적인 html을 생성할 수 없다. 해결방법으론 

 

 

 

 

설치가 완료되면 다시 시작할수있는 창이 뜨는데 다시 시작해주면 된다. 

 

기본적으로 html 파일을 사용할 수 없었으나 마켓 플레이스를 통해 웹을 설치해준 후에는 web에서 html 파일을 확인해볼 수 있다.

 

static 영역에 welcome 파일격인 html파일을 다음과 같이 작성한 후 다시 실행해준다. 

 

 

8001에 다음과같은 화면을 확인해볼 수 있다. 

 

 

 

 

 

 

 

 

 

 

 

db연동과 관련해서 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Programming > Spring Framework' 카테고리의 다른 글

Spring Security (1)  (0) 2022.04.25
Spring Boot 초기 설정  (0) 2022.04.20
5. Spring AOP (2)  (0) 2022.04.11
5. Spring AOP (1)  (0) 2022.04.11
4. Spring DI 관리  (0) 2022.04.07

 

 

 

 

 

 

 

 

 

Advice 작성하기

 

 

 

 

 

 

 

 

 

Advice의 종류


 

 

 

 

 

 

 

 

JoinPoint Interface


  • JoinPoint는 Spring AOP 혹은 AspectJ에서 AOP의 부가 기능을 지닌 코드가 적용되는 지점을 뜻하며, 모든 어드바이스는 org.aspectj.lang.JoinPoint 타입의 파라미터를 어드바이스 메소드에 첫 번째 매개변수로 선언해야 한다.
  • 단, Around 어드바이스는 JoinPoint의 하위 클래스인 ProceedingJoinPoint 타입의 파라미터를 필수적으로 선언해야 한다.

 

 

 

 

 

 

 

 

 

 

JoinPoint Interface 메소드


 

 

 

 

 

 

 

 

 

Advice 예시


 

 

 

 

 

 

 

 

 

 

 

 

 

 

Spring AOP 사용하기

 

 

 

 

 

 

 

maven : pom.xml에 라이브러리 추가


AOP 사용을 위한 라이브러리를 pom.xml에 등록한다.

 

 

 

 

 

 

 

 

servlet-context.xml 에 xmlns : aop 등록


스프링 설정 파일(servlet-context.xml)을 클릭 후 하단의 Namespaces 탭을 클릭하여 aop을 선택하여 해당 요소를 사용하기 위한 설정을 추가한다.

 

 

 

 

 

 

 

 

 

 

XML : <aop: ??? >


  • <aop:config> : AOP 설정 정보임을 나타낸다.
  • <aop:aspect> : 아스펙트를 설정한다.
  • <aop:around pointcut=“execution()”> : Around 어드바이스와 포인트컷을 설정한다

 

 

 

 

 

 

 

 

 

 

Advice를 정의하는 태그


 

 

 

 

 

 

 

 

 

 

 

Annotation : @Aspect


  • 1. 클래스 선언부에 @Aspect 어노테이션을 정의한다.
  • 2. 이 클래스를 아스펙트로 사용하려면 Bean으로 등록해야 하므로 @Component 어노테이션도 함께 정의한다.

 

 

 

 

 

 

 

 

 

 

Annotation : <aop:aspect-autoproxy>


3. aop 탭으로 가서 beans를 우클릭하고 ‘Insert <aop:aspect-autoproxy> element’를 클릭하여 <aop:aspect-autoproxy> 요소를 추가한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

Advice를 정의하는 어노테이션


 

 

 

 

 

 

 

 

 

 

Annotation Advice 예시


 

 

 

 

 

 

 

 

 

@Pointcut 어노테이션 표현식


  • Poincut 어노테이션을 사용할 때 execution 속성에 표현식을 설정해 주어야 하는데, 다음과 같은 형식으로 지정해 주어야 한다.
  • 예시의 표현식은 리턴 타입과 매개변수를 무시하고, com.greedy.biz.  패키지로 시작하는 클래스 중 이름이 Impl로 끝나는 클래스의 모든 메소드를 뜻한다.

 

 

 

 

 

 

 

 

 

 

@Pointcut 어노테이션 주요 표현식


 

 

 

 

 

 

 

@Pointcut 어노테이션 주요 표현식


 

 

 

 

 

 

 

 

'Programming > Spring Framework' 카테고리의 다른 글

Spring Boot 초기 설정  (0) 2022.04.20
Spring Boot 초기 환경설정  (0) 2022.04.20
5. Spring AOP (1)  (0) 2022.04.11
4. Spring DI 관리  (0) 2022.04.07
3. Spring IOC (2)  (0) 2022.04.06

 

 

 

 

 

 

 

 

 

Spring AOP

 

 

 

 

 

 

 

Spring AOP란


Spring AOP 란, 관점 지향 프로그래밍의 약자로 일반적으로 사용하는 클래스(Service, Dao 등) 에서 중복되는 공통 코드 부분(commit, rollback, log 처리)을 별도의 영역으로 분리해 내고, 코드가 실행 되기 전이나 이 후의 시점에 해당 코드를 붙여 넣음으로써 소스 코드의 중복을 줄이고, 필요할 때마다 가져다 쓸 수 있게 객체화하는 기술을 말한다.

 

 

 

 

 

 

 

 

 

Spring AOP 개요


아래 이미지와 같이 공통되는 부분을 따로 빼내어 필요한 시점에 해당 코드를 추가 해주는 기술을 AOP라고 말한다.

 

 

 

 

 

 

 

 

 

 

 

Spring AOP의 구조


공통되는 부분을 따로 빼내어 작성하는 클래스를 Advice라고 이야기 하며, 해당 시점을 Joinpoint, 그리고 그 시점에 공통 코드를 끼워 넣는 작업을 Weaving 이라고 말한다.

 

 

 

 

 

 

 

 

 

 

 

Aspect 란?


  • 어스펙트(Aspect)는 부가기능을 정의한 코드인 어드바이스(Advice)와 어드바이스를 어디에 적용하지를 결정하는 포인트컷(PointCut)을 합친 개념이다.
  • AOP 개념을 적용하면 핵심기능 코드 사이에 끼어있는 부가기능을 독립적인 요소로 구분해 낼 수 있으며, 이렇게 구분된 부가기능 아스펙트는 런타임 시에 필요한 위치에 동적으로 참여하게 할 수 있다.

 

 

 

 

 

 

 

 

 

 

 

Spring AOP 의 핵심 용어


 

 

 

 

 

 

 

 

 

 

 

 

 

AOP 구조 정리


 

 

 

 

 

 

 

 

 

 

 

 

 

Spring AOP의 특징

 

 

 

 

 

1) Spring은 프록시(Proxy) 기반 AOP를 지원한다.


  • Spring은 대상 객체(Target Object)에 대한 프록시를 만들어 제공하며, 타겟을 감싸는 프록시는 서버 Runtime 시에 생성된다.
  • 이렇게 생성된 프록시는 대상 객체를 호출 할 때 먼저 호출되어 어드바이스의 로직을 처리 후 대상 객체를 호출한다.
  • Proxy : 대상 객체를 직접 접근하지 못하게 '대리인'으로써 요청을 대신 받는 기술

 

 

 

 

 

 

2) Proxy는 대상 객체의 호출을 가로챈다(Intercept).


  • Proxy는 그 역할에 따라 타겟 객체에 대한 호출을 가로챈 다음 어드바이스의 부가기능 로직을 수행하고 난 후에 타겟의 핵심기능 로직을 호출 (전처리 어드바이스) 
  • 혹은 타겟의 핵심기능 로직 메서드를호출한 후에 부가기능(어드바이스)을 수행한다. (후처리 어드바이스)

 

 

 

 

 

 

 

 

 

3) Spring AOP는 메소드 조인 포인트만 지원한다.


  • Spring은 동적 프록시를 기반으로 AOP를 구현하기 때문에 메소드 조인 포인트만 지원한다. 
  • 즉, 핵심기능(대상 객체)의 메소드가 호출되는 런타임 시점에만 부가기능(어드바이스)을 적용할 수 있다.
  • 하지만, AspectJ 같은 고급 AOP 프레임워크를 사용하면 객체의 생성, 필드값의 조회와 조작, static 메서드 호출 및 초기화 등의 다양한 작업에 부가기능을 적용할 수 있다.

 

 

 

 

 

 

 

 

 

XML 기반의 aop 네임스페이스를 통한 AOP 구현


  • 부가기능을 제공하는 Advice 클래스를 작성한다.
  • XML 설정 파일에 <aop:config>를 이용해서 아스펙트를 설정한다. (즉, 어드바이스와 포인트컷을 설정함)

 

 

 

 

 

 

 

 

@Aspect 어노테이션을 이용한 AOP 구현


  • @Aspect 어노테이션을 이용해서 부가기능을 제공하는 Aspect 클래스를 작성한다. 
  • 이때 Aspect 클래스는 어드바이스를 구현하는 메서드와 포인트컷을 포함한다.
  • XML 설정 파일에 <aop:aspectj-autoproxy />를 설정한다.

 

 

 

 

 

 

 

 

 

 

 

 

'Programming > Spring Framework' 카테고리의 다른 글

Spring Boot 초기 환경설정  (0) 2022.04.20
5. Spring AOP (2)  (0) 2022.04.11
4. Spring DI 관리  (0) 2022.04.07
3. Spring IOC (2)  (0) 2022.04.06
3. Spring IOC (1)  (0) 2022.04.06

 

 

 

 

 

Spring DI 관리

 

  • DI는 IoC 구현의 핵심기술로, 컨테이너가 빈의 설정정보를 읽어봐 자동으로 해당 객체의 연결을 해준다. 
  • 이 DI 를 어떻게 실질적으로 구현할 수 있는지 코드를 통해 알아보도록 한다. 

 

 

  • 프로젝트를 생성해준다. 

 

 

 

  • 실행 할 main 메소드가 있는 클래스 Application1을 생성 해 준다. 

 

 

  • 테스트의 대상이 될 수 있는 인터페이스 Account를 먼저 생성해준다. 

 

 

  • Account를 통해 수행할 수 있는 기능을 추상적인 메소드로 작성해준다.  
  • 작성한 반환형으로 결과를 출력, 리턴 받을 수 있다. 
  • 이곳에서는 추상 메소드만이 정의됐으므로 실질적인 구현체가 필요하다. 작성해준다. 

public interface Account {
	
	/* 잔액 조회 */
	String getBalance();
	
	/* 입금 */
	String deposit(int money);
	
	/* 출금 */
	String withDraw(int money);
	
}

 

 

  • PersonalAccount 라는 클래스를 작성해준다. 이는 Account 라고하는 인터페이스를 구현할 것이다. 

 

 

implemets 사용시 클래스명에 에러가 뜰텐데 이를 클릭해 앞선 구현항목들을 작성해준다 

인터페이스를 구현한다는 것은 인터페이스에 정의된 내용들을 구현하도록 강제하는 것과 같다.

 

 

 

  • 필드에는 Account가 가져야 할 내용(은행코드, 계좌번호, 계좌비번, 잔액)들을 선언해준다. 

 

 

  • 필드를 설정한 뒤 마우스 우 클릭 > Generate Constructor using Field 선택 

 

  • 해당 되는 필드들을 확인 한 뒤 Generate 클릭

 

  • 전체 Constructor 를 만들어 주기 위해 위의 과정을 한번 더 반복한다. 
  • 다시 마우스 우 클릭 > Generate Constructor using Field
  • 유의 할 점은, 잔액 조회 없이 첫 조회를 위해 balance(잔액)을 제외한 상태로 Generate 시켜줘야 한다는 점이다. 

 

 

  • 다음과 같이 생성된 모습을 확인할 수 있다. 

 

 

  • 구현을 강제받은 메소드들의 내용을 구현한다. 
  • 먼저 잔액조회 시 리턴할 내용을 작성한다. 

 

 

3.DI - 11:11

 

 

 

 

 

 

 

public class PersonalAccount implements Account{
	
	private int bankCode;
	private String accNo;
	private String accPwd;
	private int balance;
	
	public PersonalAccount(int bankCode, String accNo, String accPwd) {
		super();
		this.bankCode = bankCode;
		this.accNo = accNo;
		this.accPwd = accPwd;
	}

	public PersonalAccount(int bankCode, String accNo, String accPwd, int balance) {
		super();
		this.bankCode = bankCode;
		this.accNo = accNo;
		this.accPwd = accPwd;
		this.balance = balance;
	}

	@Override
	public String getBalance() {
		
		return this.accNo + " 계좌의 현재 잔액은 " + this.balance + "원 입니다.";
	}

	@Override
	public String deposit(int money) {
		
		String str = "";
		
		if(money >= 0) {
			this.balance += money;
			str = money + "원이 입금되었습니다.";
		} else {
			str = "금액을 잘못 입력하셨습니다.";
		}
		
		return str;
	}

	@Override
	public String withDraw(int money) {
		
		String str = "";
		
		if(this.balance >= money) {
			this.balance -= money;
			str = money + "원이 출금되었습니다.";
		} else {
			str = "잔액이 부족합니다. 잔액을 확인해주세요.";
		}
		
		return str;
	}

	
	
	
	
	
	
	
	
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

XML 단독 사용


모든 Bean을 명시적으로 XML에 등록하는 방법

 

 

 

 

 

 

 

 

 

 

XML과 빈 스캐닝 (Bean Scanning)의 혼용


  • Bean으로 사용될 클래스에 특별한 어노테이션(Annotation)을 부여하고 Spring 컨테이너가 이를 통해 자동으로 Bean을 등록
  • 이 방식을 빈 스캐닝(Bean Scanning)을 통한 자동인식 Bean 등록기능이라고 한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Spring DI Annotation

 

 

 

 

 

Bean 등록 Annotation


@Repository, @Service, @Controller는 특정한 객체의 역할에 대한 @Component의 구체화된 형태이다.

 

 

 

 

 

 

 

Bean 의존관계 주입 Annotation


  • @Autowired 와 @Resource 어노테이션은 @Component 로 의존관계를 설정한 객체로부터 의존 관계를 자동으로 주입해주는 어노테이션이다.
  • @Autowired는 타입으로, @Resource는 이름으로 연결해주는 점이 다르다.

 

 

 

 

 

 

 

 

@Autowired


  • 정밀한 의존관계 주입 (Dependency Injection)이 필요한 경우에 유용하다.
  • @Autowired는 필드 변수, setter 메소드, 생성자, 일반메소드에 적용 가능하다.
  • 의존하는 객체를 주입할 때 주로 Type을 이용하게 된다.
  • @Autowired는 <property>, <constructor-arg> 태그와 동일한 역할을 한다.
  • @Qualifier : @Autowired 와 함께 쓰이며, 한 프로젝트 내에 @Autowired로 의존성을 주입하고자 하는 객체가 여러개 있을 경우, @Qualifier("name")를 통해 원하는 객체를 지정하여 주입할 수 있다.

 

 

 

 

 

 

 

 

@Resource


  • 어플리케이션에서 필요로 하는 자원을 자동 연결할 때 사용된다.
  • @Resource는 프로퍼티, setter 메서드에 적용 가능하다.
  • 의존하는 객체를 주입할 때 주로 Name을 이용하게 된다.

 

 

 

 

 

 


@Value


  • 단순한 값을 주입할 때 사용되는 어노테이션이다.
  • @Value(“Spring”)은 <property .. value=“Spring” /> 와 동일한 역할을 한다.

 

 

 

 

 

 

 

<context:component-scan> 태그


  • @Component를 통해 자동으로 Bean을 등록하고, @Autowired로 의존관계를 주입받는 어노테이션을 클래스에서 선언하여 사용했을 경우에는 해당 클래스가 위치한 특정 패키지를 Scan하기 위한 설정을 XML에 해주어야 한다.

 

 

 

 

 

 

<context:component-scan> 태그 예시


<context:include-filter>태그와 <context:exclude-filter>태그를 같이 사용하면 자동 스캔 대상에 포함시킬 클래스와 포함시키지 않을 클래스를 구체적으로 명시할 수 있다.

 

<context:component-scan base-package=“com.korea.firstSpring" />

 

 

 

 

 

 

 

 

 

'Programming > Spring Framework' 카테고리의 다른 글

5. Spring AOP (2)  (0) 2022.04.11
5. Spring AOP (1)  (0) 2022.04.11
3. Spring IOC (2)  (0) 2022.04.06
3. Spring IOC (1)  (0) 2022.04.06
2. Spring Framework  (0) 2022.04.06

 

 

구조 


 

 

 

 

 

환경 설정


 

 

 

 

 

 

 

 

Chap01-bean-factory : section01

 

 

 

 

 

 Application

package com.greedy.section01.xmlconfig;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Application {

	public static void main(String[] args) {
		
		/* BeanFactory란?
		 * 스프링 컨테이너의 최상위 컨테이너이며, ApplicationContext와 함께 
         스프링 컨테이너라고 한다.
		 * Bean의 생성과 설정, 관리 등의 역할을 맡고 있다.
		 * */
		
		/* MemberDTO(POJO) 클래스와 spring-context(Configuration) XML 설정 정보를 이용해서
		 * bean 등록, 생성 예제 확인
		 * */
		
		/* ApplicationContext의 하위 구현체인 GenericXmlApplicationContext는 
        XML 설정 메타정보를 읽어와서
		 * BeanFactory를 동작 시킨다.
		 * GenericXmlApplicationContext는 설정 메타 정보가 담긴 XML 파일의 경로를 
         클래스패스 하위 경로 전부 기술해야 설정 파일을 읽어온다. 
		 * */
         
		ApplicationContext context 
        = new GenericXmlApplicationContext("com/greedy/section01/xmlconfig/spring-context.xml");
		
		/* 1. bean의 id를 이용해 bean을 가져오는 방법
		 * id를 이용하는 경우 bean의 정확한 타입을 유추할 수 없기 때문에 Object 타입으로 
         반환하므로 다운캐스팅 필요 
		 * */
		//MemberDTO member = (MemberDTO)context.getBean("member");
		
		/* 2. bean의 클래스 메타 정보를 전달하여 가져오는 방법
		 * 가져오려는 bean의 타입이 명확하기 때문에 형변환이 필요 없다.
		 * */
		//MemberDTO member = context.getBean(MemberDTO.class);
		
		/* 3. bean의 id와 클래스 메타 정보를 전달하여 가져오는 방법
		 * */
		MemberDTO member = context.getBean("member", MemberDTO.class);
		
		System.out.println(member);
		
		
	}

}

 

 

 

 


 

  • ApplicationContext은 BeanFactory 를 확장한 여러 인터페이스를 통합해서 상속받은 인터페이스
  • 인터페이스이다보니 그 자체로 실질적인 어떤 구현체를 생성할 수 없음
  • 해서 ApplicationContext 안에있는 강제화된 기능들을 구현해놓은 구현체인 GenericXmlApplicationContext
  • GenericXmlApplicationContext는 XML 설정파일을 읽어서 구동이 되는 클래스

 

정의되어있지않기에 뜨는 화면

 

 

 

  • 우선 Spring Context를 추가한다. 그 안에  ApplicationContext 과 GenericXmlApplicationContext 등이 정의되어있음
  • mvn 레파지토리에서 라이브러리를 다운받아옴으로서 추가 해 볼 것이다. 

 

 

mvnrepository.com

 

 

  • release 된 버전 중 가장 사용 빈도가 높은 5.2.9 버전을 클릭한 후 .jar  파일을 다운 받는다. 
  • * 유의할 점, Spring 모듈 끼리는 버전을 맞춰주어야 한다. 현재 5.2.9 버전을 사용한다면 앞으로 추가하는 모듈들 끼리의 버전은 5.2.9버전으로 맞춰 주어야 한다는 뜻이다.  그래야 충돌을 미연에 방지할 수 있다. 

 

 

 

  • 이후 프로젝트 내 lib라는 폴더를 만들어 다운 받은 파일을 옮겨 붙여 준다. 

 

 

  • 이렇게 해줄 시 물리적으론 jar 파일이 프로젝트 내 존재하는 것은 맞지만, 실질적인 이 프로젝트의 사용 라이브러리로 등록되어있는 것은 아니다. 

 

 

  • 프로젝트 우클릭 > Properties > Java Build Path 카테고리 > Library 탭 > Classpath 선택 > Add JARs... 선택

 

  • 물리적으로 존재하는 폴더 내 .jar 파일을 선택 후 확인을 눌러준다. 

추가된 모습

 

 

  • 라이브러리 빌드 등록 후 두가지 기능이 정상적으로 import 가 가능해지는 것을 확인할 수 있다. 

 

 

  • 생성을 할 인스턴트 쪽에 설정 메타정보를 기재해 준다. 
  • 여기에 기재된 정보는 spring-context.xml 로 만든 bean configuration 파일이다. 
  • IoC 컨테이너를 만들때 이러한 설정정보를 쓰라고 전달하는 의미이다. 

 

 

  • 그런데, 작성한 설정 정보에 에러줄이 발생한다. 왜일까? 이는 스프링의 구성모듈과 관련있다. 

 

 

  • 방금 Context를 추가를 했는데 다음으로 Context 내에서 Core 안에있는 어떤 기능을 쓰려고 한다. 
  • xml 파일이 읽어 올때, 리소스를 처리할 수 있는 기능이 이 Core 안에 기재되어있기 때문에 사실상 Context 만으로는 동작하기 어렵다. 
  • 즉, 좀 더 추가해야하는 기능들이 더 있다는 의미이다. 

 

더보기

Spring의 구성모듈

 

 

 

 

 

 

 

Spring Core 추가

 

  • 마찬가지로 mvn 레파지토리를 이용한다.
  • 5.2.9 버전을 jar 파일을 동일하게 다운 받은 후 lib 물리 구조에 붙여넣기 한다. 

 

 

  • 추가이후 build path를 통해 라이브러리 등록을 해준다. 

 

 

 

  • 이후 사라진 에러를 확인할 수 있다. 
  • 이 설정정보를 전달받은 스프링 컨테이너가 생성될 수 있는지를 테스트 해보자.

 

 

콘솔창의 에러를 확인한다. 현재 springframework 아래 beans 라는 경로를 찾지못했음을 알 수 있다. 

 

 

  • Beans 가 없다는 의미이다.
  • 이런식으로 콘솔창을 통해 어떤 기능이 포함되지못했는지 하나씩 유추해가는 방식이 필요하다. 

 

 

  • MVN 레파지 토리에서 Spring Beans를 가져온다. 
  • 버전을 맞추어 다운 받는 것에 유의한다. 
  • 앞서 했던 것과 마찬가지로 lib 에 다운받은 파일을 추가후 build path 에서 추가해준다. 

 

 

  • 추가 이후 다시 실행했더니 다음과같은 에러가 콘솔창에 떴다. 

 

 

 

  • 마찬가지로 MVN에서 commons logging을 가져오도록 한다. 

 

 

  • 1.2 버전을 다운받는다. 마찬가지로 빌드 패스까지 마무리 지어준다.

 

  • 파싱처리에 필요한 Spring expression 이 없음을 확인할 수 있다. 
  • 마찬가지로 다운받은 후 빌드패스까지 해준다.

 

  • 이와같이 라이브러리 추가를 완료한 후 실행해 주었을때 (이전 에러 출력값이 사라지고) 콘솔창에 아무것도 뜨지않음으로서 GenericXmlApplicationContext 의 객체가 성공적으로 생성되었음을 알 수 있다. 

 

 

정리

- ApplicationContext의 하위 구현체인 GenericXmlApplicationContext는 XML 설정 메타정보를 읽어와서 BeanFactory를 동작 시킨다.
- GenericXmlApplicationContext는 설정 메타 정보가 담긴 XML 파일의 경로를 클래스패스 하위 경로 전부 기술해야 설정 파일을 읽어온다. 

 

 

 

  • 여기까지 왔을 때, 이 설정정보가 잘 만들어졌음을 어떻게 알 수 있을까? 
  • 현재 spring-context.xml 안에는 bean이 있는데, 이 bean 이 잘 등록되어, 생성되었는지를 확인 해본다.

 

  • bean을 꺼내오는 첫번째) getBean 메소드
  • bean에는 고유 아이디가 있는데, 이때 "member"라는 아이디를 가진 bean을 리턴하도록 한다.

 

  • 타입 미스매치 에러가 뜬다. 이유는 고유 id만으로 타입을 유추할 수 없기 때문이다. (타입=/=아이디)
  • 유추할 수 없는 타입은 Object로 리턴이 되기 때문에, 원하는 타입으로 리턴값을 담기위해서는 다운캐스팅이 필요하다. 

 

 

  • 객체가 잘 등록되어서 생성되었다는 것을 콘솔창에서 확인 해 볼 수 있다.

 

 

  • 우리는 다음의 과정을 거치면서 MemberDTO member = new MemberDTO(1, "user01"...) 과 같은 객체를 생성한 적이 없다.
  • 그러나 콘솔창을 보면 생성되었음을 알게되는데, 이건 누가 해준걸까? 바로 ApplicationContext 이다.
  • spring-context 안에 MemberDTO를 다음과같이 등록한다는 정보를 작성해주었기 때문이다. 
  • 이로서 빈을 생성했고, 잘 읽어 올 수 있었음을 확인하였다.

spring-context.xml

 

 

 

 

Bean 가져오는 방법 세가지


1. bean의 id를 이용해 bean을 가져오는 방법 
- id를 이용하는 경우 bean의 정확한 타입을 유추할 수 없기 때문에 Object 타입으로 반환하므로 다운캐스팅 필요 
MemberDTO member = (MemberDTO)context.getBean("member");

 

 

2. bean의 클래스 메타 정보를 전달하여 가져오는 방법
- 가져오려는 bean의 타입이 명확하기 때문에 형변환이 필요 없다.
  • 애초에 MemberDTO 타입의 bean을 달라는 의미이다. 
  • 즉, 반환타입이 Object 가 아니므로 다운캐스팅이 필요없다.

 

 

3. bean의 id와 클래스 메타 정보를 전달하여 가져오는 방법
   MemberDTO member = context.getBean("member", MemberDTO.class);
   System.out.println(member);
  • 만약,  MemberDTO 타입의 bean이 여러개라면, 2번의 방법이 무의미하다.
  • 이럴때 빈의 아이디와 클래스 정보를 둘 다 기입한다. 역시 클래스를 기입해주었으므로 다운캐스팅은 필요없다.

 

 

 

정리

1. 제어의 역전을 구현하기 위해 Bean Factory에 빈을 등록하여 사용한다.
2. GenericXmlApplicationContext는 설정 메타 정보가 XML 에 만들어져있는 환경이다. 

 

 

 

 

 

 

 

 

 


 

 

 

  • XML에 설정 메타정보가 기입되었던것과 마찬가지로, java-config 파일에서도 똑같이 동작하는지 그 방법과 결과를 살펴보도록 한다.
  • 테스트할 클래스를 만들어준다. 

 

 

  • 동일한 테스트를 위해 MemberDTO 파일을 복사해와 경로만 수정해준다. 
  • 이 DTO 파일이라는 오브젝트를 사용하기위해 설정 메타 정보라는 것이 필요하다.
  • 아까는 그 정보를 XML에 파일에 작성했다면 이번엔 자바 클래스에 만들어 보도록 한다.

 

 

  • 컨테이너를 만들때 전달할 설정정보를 정의할 클래스 정의

 

 

  • 다음과같이 생성된 ContextConfiguration 파일에 @Configuration 이라는 어노테이션을 붙인다.
  • 즉, 이 클래스가 설정 메타 정보를 가지고 있다는 의미를 가진 어노테이션을 추가한다.
  • 컨테이너를 생성할 시 해당 어노테이션이 달린 클래스를 먼저 인식하여 컨테이너를 설정한다.

 

 

  • xml 파일에서 빈을 등록했듯이, 자바 클래스 파일에서도 빈을 등록할 수 있어야 한다.
  • 여기서는 '메소드' 형식을 사용한다.
  • 리턴타입이 MemberDTO, 리턴타입의 객체에 원하는 정보를 new 생성자를 통해 설정한다.

 

  • bean을 등록하기 위해서는 @Bean 어노테이션을 이용한다.
  • 즉, 메소드 위에 'Bean'임을 알 수 있도록 @Bean 어노테이션을 붙여준다.

 

  • 앞서 xml에서 빈의 정보를 id로 알았다. 여기서는 어떻게 해야 id의 역할을 할 수 있을까?
  • 이름을 등록하기 위해 @Bean(name="member") 와 같이 사용한다.
  • 만일 bean에대한 아이디를 지정하지 않으면 [메소드명]을 사용하게되니 유의한다.

 

 

  • 다음과같이 작성이 되어야한다.
@Configuration
public class ContextConfiguration {

	@Bean(name="member")
	public MemberDTO getMember() {
		
		return new MemberDTO(1, "user01", "pass01", "배산임수");
	}

}

 

 

정리

- @Bean(name="myName") 혹은 @Bean("myName")을 이용하여 bean의 id를 설정할 수 있다.
- 이 때 bean의 이름을 지정하지 않으면 메소드의 이름을 bean의 id로 자동 인식한다.

 

 


 

 

 

  • Application 에서실행하여 마찬가지로 빈이 잘 생성되고 등록되었는지 확인한다. 
  • 똑같이 스프링 컨테이너를 만들기 위해 ApplicationContext context 을 사용하는 것은 동일하다. 
  • 그러나 앞서 xml 파일로 테스트 했다면 이번엔 자바 클래스 파일을 통해서 테스트 하는 것이기 때문에 메타 정보를 연결하는 객체가 달라야 한다. 그러한 설정정보를 읽어왔을때 사용하는 구현체도 달라진다. 
  • 즉, GenericXmlApplicationContext 구현체가 아닌  AnnotationConfigApplicationContext 을 사용하게 된다. 
  • 여기에 방금작성한 설정정보가 담긴 ContextConfiguration.class 라는 클래스(타입)을 전달한다.

 

 

- AnnotationConfigApplicationContext라는 어노테이션 설정 정보를 읽어서 컨테이너 설정을 하는 구현체 이용한다.
- 인자로 @Configuration 어노테이션이 달린 설정 클래스의 메타 정보를 전달하여 인스턴스를 생성한다.

 

 

  • Bean이 잘 등록됐는지 확인하기 위해 context를 통해 빈을 꺼내온다.
  • 앞서 했던 빈을 꺼내오는 세가지 방법중 이름과 타입을 정확히 기입해서 꺼내오는 방법을 사용

 

 

  • 위를 실행할시 콘솔창에 다음과같이 aop가 없음을 확인할 수 있다.
  • AnnotationConfigApplicationContext 의 기능을 이용하기 위해서 스프링의 aop라는 기능이 필요함을 알 수 있다.

 

  • 앞서 해왔던 대로 MVN 레파지 토리 -> Spring AOP 검색 -> 5.2.9 버전 다운 로드 -> Build Path 등록

 

  • 라이브러리가 잘 build path 등록되었다면 콘솔창에서 다음과같은 결과를 확인할 수 있다.
  • xml-config 에서 확인했던것과 마찬가지로 컨테이너에서 빈이 잘 생성되고 등록된것을 알 수 있다.

 

 

 

정리 

- 제어의 역전, 빈 팩토리를 통해서 빈을 등록할 수 있다. 
- 빈 등록은 xml 을 통한 방식도 있고 자바 클래스를 통한 방식도 있다. 
- 각 방식에 따라서 실제 내부적인 구현체 역시 달라진다. 
- 그렇지만 구현체가 달라져도 다음 사실은 동일하다 : 설정메타정보를 읽어와서 생성이 된 컨테이너 안에는 -개발자가 직접적으로 객체를 생성하는 것이 아니라- 컨테이너 안에서 관리되는 Bean 객체로서 존재를 하게 된다. 

 

 

Configuration Metadata를 작성하는 방법에 대해 서술된 부분을 보자

 

 

 

  • 다음으로 Component Scan을 통해 Bean이 등록되는 모습을 살펴보자.
  • 특정 어노테이션을 이용해 Bean으로 등록해줄 것을 표시할 수 있는데 바로 @Component 이다. 

 

 

 

 

 

 

 

 

MemberDTO

public class MemberDTO {
	
	private int sequence;
	private String id;
	private String pwd;
	private String name;
	
	public MemberDTO() {}

	public MemberDTO(int sequence, String id, String pwd, String name) {
		super();
		this.sequence = sequence;
		this.id = id;
		this.pwd = pwd;
		this.name = name;
	}

	public int getSequence() {
		return sequence;
	}

	public void setSequence(int sequence) {
		this.sequence = sequence;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "MemberDTO [sequence=" 
        + sequence + ", id=" + id + ", pwd=" + pwd + ", name=" + name + "]";
	}
	
	

}

 

 

 

spring-context.xml

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

	<!-- MemberDTO bean 등록 -->
	
	<bean id="member" class="com.greedy.section01.xmlconfig.MemberDTO">
		<constructor-arg index="0" value="1"/>
		<constructor-arg name="id" value="user01"/>
		<constructor-arg index="2"><value>pass01</value></constructor-arg>
		<constructor-arg name="name"><value>홍길동</value></constructor-arg>
	</bean>

</beans>

 

 

 

 

 

 

 

 


 

 

 

 

 

Chap01-bean-factory : section02

 

 

 

 

 

Application 

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
	
	public static void main(String[] args) {
		
		/* AnnotationConfigApplicationContext라는 어노테이션 설정 정보를 읽어서
        컨테이너 설정을 하는 구현체 이용한다.
		 * 인자로 @Configuration 어노테이션이 달린 설정 클래스의 메타 정보를
         전달하여 인스턴스를 생성한다.
		 * */
		ApplicationContext context 
        = new AnnotationConfigApplicationContext(ContextConfiguration.class);
		
		MemberDTO member = context.getBean("member", MemberDTO.class);
		
		System.out.println(member);
	}

}

 

 

ContextConfiguration 

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/* 이 클래스가 설정 메타 정보를 가지고 있다는 의미를 가진 어노테이션을 추가한다.
 * 컨테이너를 생성할 시 해당 어노테이션이 달린 클래스를 먼저 인식하여 컨테이너를 설정한다.
 * */
@Configuration
public class ContextConfiguration {
	
	/* bean을 등록하기 위해서는 @Bean 어노테이션을 이용한다.
	 * @Bean(name="myName") 혹은 @Bean("myName")을 이용하여 bean의 id를 설정할 수 있다.
	 * 이 때 bean의 이름을 지정하지 않으면 메소드의 이름을 bean의 id로 자동 인식한다.
	 * */
	@Bean(name="member")
	public MemberDTO getMember() {
		
		return new MemberDTO(1, "user01", "pass01", "배산임수");
	}

}

 

 

MemberDTO

public class MemberDTO {
	
	private int sequence;
	private String id;
	private String pwd;
	private String name;
	
	public MemberDTO() {}

	public MemberDTO(int sequence, String id, String pwd, String name) {
		super();
		this.sequence = sequence;
		this.id = id;
		this.pwd = pwd;
		this.name = name;
	}

	public int getSequence() {
		return sequence;
	}

	public void setSequence(int sequence) {
		this.sequence = sequence;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "MemberDTO [sequence=" + sequence + ",
        id=" + id + ", pwd=" + pwd + ", name=" + name + "]";
	}
	
	

}

 

 

 

 

 

 

 

 

 

파일구조 


 

 

 

 

 

 

 

 


 

 

 

 

 

 

chap02-component-scan : section01

 

  • 상단 테스트에서 진행했던 라이브러리를 그대로 가져와 Build Path 지정해주는 작업까지 완료한다. 
  • 코드 작성을 위해 다음과같은 javaconfig 클래스를 작성한다.

 

 

 

ComponentScan 기능을 이용한 bean 등록 설정해보기 


  • ComponentScan이란? base-package로 설정 된 하위 경로에 특정 어노테이션을 가지고 있는 클래스를 이용하여 bean으로 등록한다.
  • @Component 어노테이션이 작성 된 클래스를 인식하여 bean으로 만들게된다.
  • @Component는 좀 더 상위의 개념이며, 특수 목적에 따라 세부 기능을 제공하는 @Controller, @Service, @Repository, @Configuration 등을 인식한다.
  • 즉, @Component 어노테이션이 작성된 클래스의 빈을 스캔하여 그것을 빈으로서 등록해주는 것이다. 

 

  • 테스트를 위한 설정 : MemberDTO 를 그대로 가져와 패키지만 변경해준다. 

 

  • 다음으로 MemberDAO라는 이름의 인터페이스를 작성한다. 

 

 

  • 인터페이스는 추상메소드를 작성하는 공간이며, 구조적인 부분을 강제화 할 수 있는 기능이다. 
  • 여기서는 필요로하는 두가지 기능을 작성해본다.  1) 회원정보 조회 메소드 2) 회원정보 저장 메소드

 

 

  • 인터페이스에서 작성한 내용을 강제화하기 위한 기능인 MemberDAOImpl 을 만들어준다.
  • 인터페이스의 장점 1) 인터페이스를 이용하면 기능을 구현할 것을 강제화할 수 있다는 점과, 2) 기능에 변화가 있을때마다 해당 인터페이스를 사용하는 클래스와의 결합도를 느슨하게 해준다. (유지보수 용이)

 

- 인터페이스로 메소드를 강제화 한 후 구현하여 사용하면 강제성이 부여 된다.
- 또한 결합 관계를 느슨하게 만들 수 있다.


 

 

  • db 연동을 하는 것이 아니므로 데이터를 다음과같이 적어준다.

 

  • 매개변수로 전달받은 회원 번호를 map에서 조회 후 회원 정보를 리턴해주는 용도의 메소드

 

  • 매개변수로 전달받은 회원 정보를 map에 추가하고 성공 실패 여부를 boolean으로 리턴하는 메소드
  • 전달받은 멤버의 정보중에서 시퀀스에 해당하는 값을 키값으로, 나머지 객체값(newMember)은 VALUE 값으로 해서 MAP 에 넣겠다는 것.  
  • MAP은 동일한 값은 중복저장하지 않으므로, 잘 저장되었는지는 MAP의 사이즈 (.size) 를통해서 알아보도록 한다.
  • 사이즈가 늘어났다면 true 를, 그렇지않다면 false 를 반환할 것이다. 

 

 

 

  • 스프링 컨테이너가 스캐닝 기능을 이용하여 해당 클래스를 bean으로 등록할 수 있는 어노테이션을 설정한다. 
  • value 속성을 이용하면 bean의 id를 설정할 수 있으며, value는 생략 가능하다.
  • 이름(id)를 설정하지 않으면 Class의 앞 글자를 소문자로 하여 bean을 생성한다.
  • @Controller, @Service, @Repository와 동일한 기능을 가지지만 각 계층을 표현하는 어노테이션은 특정 용도에 맞는 부가적인 혜택이 있으니 게층별로 구분하여 사용하는 것이 좋다. (계층이 명확하지않다면 @Component를 사용해도 좋다.)

 

클래스 위에 @Component 어노테이션을 붙여준다. 그럼 나중에 이러한 어노테이션이 붙은 대상을 가지고 bean 등록할 수 있다.

 

  • 이때, VALUE 속성을 통해 해당 컴포넌트의 이름을 지어줄 수 있다.
  • 만약, VALUE 속성을 지정해 주지않았다면? 마찬가지로 메소드 명을 빈의 이름으로 자동 지정될 것이다. 
  • memberDAOImpl > 앞글자를 소문자로 만들어 빈의 이름으로 등록했을 것이다. 

 

 

  • 다음으로 필요한 것은 설정파일이다.

 

  • 설정파일임을 스프링 컨테이너에서 알 수 있도록 @Configuration 어노테이션을 붙여준다.
  • 단, MemberDAOImpl 에 작성한 @Component 를 잘 스캔 해 빈으로 등록해주는지 확인하는 것이 이번 테스트의 목적이므로, 해당 설정파일에는 아무것도 적지않는다.

 

 

  • Application1 으로 돌아온다. 만들어야 하는 것은 <스프링 컨테이너>에 해당하는 것이다. 
  • 어노테이션 기반으로 스프링 파일을 읽어와서 스프링 컨테이너를 만들겠다는 의미이다. 
  • 전달한 값은 따로 빈 등록을 하지않은 빈 껍데기 설정파일이다. 
  • 여기서 기대하는 것: @Component 어노테이션이 스캔시 '나를 빈으로 등록해라' 라고 하는 어노테이션이기 때문에 빈으로 잘 등록이 되는지 확인

 

 

  • context 안에 담겨있는, 정의된 빈의 이름들을 배열로 가져온다.
  • 해당 배열안에 어떤 빈들이 등록되어있는지는 반복문을 통해서 출력 해본다.

 

 

  • 실행시 다음과같은 결과를 콘솔창에서 확인할 수 있다.
  • 앞글자가 소문자로 바뀐 설정 파일 클래스는 확인할 수 있지만, 기대했던 클래스는 빈 등록이 되지않은 것 같다.
  • 그렇다면 왜 등록이 되지않은 것일까? 

 

 

  • Context Configuration 은 어떤 기준으로 스캔을 하고 있는걸까? 
  • 본인이 현재 중재하는 경로 기준, 경로가 지엽적이라면 (base package를 설정하지않았다면) 본인의 패키지를 기준으로 스캔을 한다. 즉, 스캔이 안된것이 문제이다. 
  • 그렇다면 base-package에 대한 설정을 추가를 해주도록 한다. 

 

 

  • 다시 설정파일로 돌아와 다음과같은 어노테이션을 추가로 붙여준다. -> @ComponentScan
  • basePackages에 등록되지 않은 패키지는 스캔에서 제외하고, 등록된 패키지 내의 @Component 어노테이션을 탐색한다.
  • 이 때 basePackage를 등록하지 않으면 현 설정 클래스가 존재하는 패키지를 자동 basePackage로 설정한다. 

 

 

  • 위와같은 어노테이션을 붙여줌으로서, 명시적으로 탐색할 기준이 되는 베이스 패키지를 설정해 주게 되었다. 
  • 이제 config 폴더내 설정파일만 탐색하는 것이 아니라, javaconfig 하위의 모든 어노테이션을 탐색할 수 있게 되었다.

 

 

  • 베이스 패키지 설정 후 다음과 같이 MemberDAO 가 빈으로 등록되었음을 확인할 수 있다. 

 

 

  • 빈등록이 잘 되었다면 좀 전에 작성한 메소드를 이용해보는 것도 가능하다. 
  • 1번 멤버 조회와 3번 멤버 인서트, 3번 멤버 조회를 차례로 작성한다.

 

  • 콘솔창에 출력된 모습

 

 

 

 

ComponentScan


스프링 컨테이너는 빈들의 생명주기를 관리하며, 그러한 설정 정보를 설정하는 방식을 지금까지 살펴보았다. 

ComponentScan 어노테이션에 basePackages 외에 더 추가할 수 있는 방법들을 알아보고자 한다.

 

테스트를 위해 별도의 경로를 작성해준다. Application2 라는 이름의 javaconfig 작성

 

 

 

  • 테스트를 위한 ApplicationContext 를 생성한다.
  • 앞선 테스트와같이 설정 정보를 저장하기 위한 Context Configuration를 전달한 뒤 에러밑줄을 선택해 만들어 준다. 

 

  • 만들어진 클래스에는 이것이 [설정정보]임을 나타내는 어노테이션 @Configuration 을 달아준다.
  • 또한 basePackages는 다음과같이 작성한다.

 

  • 추가할 수 있는 기능은 다음과같다. 베이스 패키지를 하나이상 설정할 수 있다는 것 
  • 어노테이션에 추가하는 것은 문법적으로 [ 콤마, ] 로 나타낸다.
  • 만약 단일 값인 경우엔 패키지명을 포함한 클래스이름을 나열하면 되지만, 하나 이상의 베이스 패키지인 경우 {} 중괄호를 이용하여 나열한다. (중괄호=나열)

 

  • 베이스 패키지를 설정하면 그 하위의 파일들은 전부다 스캔이 된다. 그러나 관련없는 클래스까지 스캔하는 것은 어떤 관점에서는 낭비이므로, 임의의 타입은 스캐닝에서 제외시킬 수 있다. 
  • excludeFilter로 스캐닝에서 제외할 타입을 기술하면 해당 타입은 스캐닝에서 제외한다.

 

1. 타입으로 설정

  • ASSIGNABLE_TYPE이란 것은 타입을 지정한다는 것이다.
  • 스캐닝에서 제외할 클래스를 다음과같이 작성해주면 된다.
  • 단, 같은 패키지 하위에 같은 베이스 패키지를 기술하면 오류의 원인이 될 수 있으므로 사전에 중복된 베이스 패키지는 주석 처리한다.
    @Configuration
    @ComponentScan(basePackages="com.greedy.section01.javaconfig", 
         excludeFilters= {
             @ComponentScan.Filter(

             type=FilterType.ASSIGNABLE_TYPE,
             classes= {MemberDAO.class}
     		})
    public class ContextConfiguration2 {
    }

 

  • 앞선 설정파일의 빈 등록 코드를 그대로 가져와 실행해 주면 콘솔창에 다음과같이 뜬다. 
  • MemberDAO.class를 찾을 수 없다는 의미이므로, 컴포넌트 스캔에 잘 제외되었음을 알 수 있다.

 

 

2. 어노테이션 종류로 설정

  • Component  어노테이션이 붙어있는 클래스를 스캔하지않겠다는 의미
    @Configuration
    @ComponentScan(basePackages="com.greedy.section01.javaconfig", 
         excludeFilters= {
             @ComponentScan.Filter(

             type=FilterType.ANNOTATION,
			 classes= {org.springframework.stereotype.Component.class}
             })
    public class ContextConfiguration2 {
    }

 

 

3. 표현식으로 설정

REgular Expression 표현식으로 타입을 설정한 후, section01 하위의 모든 클래스를 제외하겠다는 의미

    @Configuration
    @ComponentScan(basePackages="com.greedy.section01.javaconfig", 
         excludeFilters= {
             @ComponentScan.Filter(

             type=FilterType.REGEX,
			 pattern= {"com.greedy.section01.*"}
             
    public class ContextConfiguration2 {
    }

 

 

정리 

- 컴포넌트 스캔이라는 기술을 사용할 때, base package 하위의 모든 클래스를 스캔하는 것이 시간이 소요될 수 있으니 이 스캐닝과 관련이 없는 대상은 제외해주는 옵션을 사용할 수 있다. : [ , excludeFilters = { } ]
- 그 옵션을 사용하는 방법은 다양한데 세가지만 살펴보았다 : 1. 타입 2. 어노테이션 3. 표현식으로 설정

 

 

 

 

 

또 다른 테스트를 위해 같은 경로 내에 ContextConfiguration3 을 만들어준다. 

 

 

 

  • 설정 메타 정보 클래스를 만드는 것이 용도이다. 
  • 1) basePackages의 기본 설정 경로를 지정하고 2) useDefaultFilters를 false로 하면 모든 어노테이션을 스캔하지 않는다.
  • 이 때 스캔할 대상 클래스만 따로 지정할 수 도 있다.
  • excludeFilters 가 제외하는 방식이라면, includeFilters 는 포함하는 방식이다. 
  • exclude 필터 설정하는 방식과 동일하다
  • useDefaultFilters=false : 기본적으로 필터링 하지 않음을 의미



  • 확인 테스트를 위한 Application3을 만들고 코드는 (1,2) 그대로 유지한다.
  • MemberDAO 만 빈으로 생성되고 등록되었음을 확인할 수 있다.

 

 

 

 

ContextConfiguration 을 XML 형태로 테스트

javaconfig / xmlconfig 호환해서 테스트 해보기 위함

다음 클래스를 작성한다. 

 

 

  • 앞서 작성한 기능을 그대로 사용하여 테스트 해보기 위해 section01 에서 위 코드르를 그대로 가져온다. 
  • 복사 붙여넣기 후 패키지명 변경

 

 

  • xml 설정 정보를 만들기위해 Spring 컨테이너와 그 xml 을 읽어올 수 있는 GenericXmlApplicationContext를 생성한다. 

 

 

 

  • 설정 메타정보 경로 작성

 

  • 경로를 작성한 메타 정보 파일은 실제 존재하지 않으므로 다음과같은 단계를 통해 만들어주도록 한다.

 

  • 해당 xml 파일에 설정하고 싶은 정보는 다음과같다. 
  • @Component 혹은 @Repository 등이 기재된 클래스의 [빈스캐닝]을 해올 것
  • 자바 config 에서 어노테이션을 붙여 작성했다면, xml에서는 태그를 이용해 다음과같이 작성한다. 
  • 컴파일 에러 줄이 뜨는 이유 : Context 의 sckima 스키마가 정의되어있지 않기 때문

 

  • 에러 없애는 법은 다음과 같다. 
  • 하단의 Namespace 클릭 > beans 와 context 체크 선택 > 다시 Source 로 돌아와 에러가 사라진 것을 확인

 

 

  • 스키마 정의 후 이전엔 존재하지 않았던 스키마정의들을 확인할 수 있다. 
  •  context 스키마가 있어야 정의할 수 잇으므로 하단의 namespaces 탭에서 context 스키마를 추가하고 작성할 것

 

  • 이러한 xml 에서의 설정은 하단의 자바 클래스에서 작성한 어노테이션과 의미적으로 다르지않다. 

 

  • 실행하면 다음과같은 콘솔창을 확인할 수 있다. 
  • xml 설정파일의 빈 스캐닝 하기위한 설정에 따라서 xmlconfig 하위의 클래스들을 스캐닝한 후,
  • MemberDAO 에 작성된 어노테이션을 읽어 들여서 ApplicationContext context = ... 가 빈 등록후 생성을 한다. 

 

 

 

 


 

 

  • 앞서 했던 것과 마찬가지로, 자바 클래스가 아닌 xml config 파일에서 includeFilters 와 excludeFilters 를 어떻게 사용하는지 살펴본다. 
  • 1번 파일을 그대로 붙여넣기하여 2번 파일을 생성한다. 

 

  • context: 뒤에 올 알맞은 엘리먼트로 다음과같은 추천이 뜬다. 

 

  • exclude-filter 를 정의하기위해 다음과같이 작성한다. 
  • type = 타입이 무엇인지 / expression = "빈 스캐팅에서 제외할 경로"

 

  • 위 xml 설정과 자바 config 에서 코드로 작성했던 내용을 비교대조 해볼 수 있다. 의미적으론 일치한다.

 

  • 필터링에서 빠지는지 확인할 어플리 케이션 클래스 작성

 

  • exclude 필터를 넣어 빈 스캐닝에서 제외된 것을 확인할 수 있다. 

 

 

 

 

 

 

 

 

 

ContextConfiguration1 

package com.greedy.section01.javaconfig.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/* basePackages에 등록되지 않은 패키지는 스캔에서 제외하고, 등록된 패키지 내의
@Component 어노테이션을 탐색한다.
 * 이 때 basePackage를 등록하지 않으면 현 설정 클래스가 존재하는 패키지를 자동 
 basePackage로 설정한다. */
 
//@Configuration
//@ComponentScan(basePackages="com.greedy.section01.javaconfig")

public class ContextConfiguration1 {

}

 

 

ContextConfiguration2 

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

import com.greedy.section01.javaconfig.MemberDAO;

/* excludeFilter로 스캐닝에서 제외할 타입을 기술하면 해당 타입은 스캐닝에서 제외한다. */
//@Configuration
//@ComponentScan(basePackages="com.greedy.section01.javaconfig", 
//		excludeFilters= {
//			@ComponentScan.Filter(
//					/* 1. 타입으로 설정 */
//					//type=FilterType.ASSIGNABLE_TYPE,
//					//classes= {MemberDAO.class}
//					/* 2. 어노테이션 종류로 설정 */
//					//type=FilterType.ANNOTATION,
//					//classes= {org.springframework.stereotype.Component.class}
//					/* 3. 표현식으로 설정 */
//					//type=FilterType.REGEX,
//					//pattern= {"com.greedy.section01.*"}
//					)
//		})
public class ContextConfiguration2 {

}

 

 

 

ContextConfiguration3

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

import com.greedy.section01.javaconfig.MemberDAO;

/* basePackages의 기본 설정 경로를 지정하고 useDefaultFilters를 false로 
하면 모든 어노테이션을 스캔하지 않는다.
 * 이 때 스캔할 대상 클래스만 따로 지정할 수 도 있다.
 * */
@Configuration
@ComponentScan(basePackages="com.greedy.section01.javaconfig",
			   useDefaultFilters=false,
			   includeFilters= { @ComponentScan.Filter(
					   	/* exclude 필터 설정하는 방식과 동일하다 */
					   	type=FilterType.ASSIGNABLE_TYPE,
					   	classes= {MemberDAO.class}
					   )})
public class ContextConfiguration3 {

}

 

 

 

 

 

 


 

 

 

 

 

 

Application1

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.greedy.section01.javaconfig.config.ContextConfiguration1;

public class Application1 {

	public static void main(String[] args) {
		
		/* ComponentScan 기능을 이용한 bean 등록 설정
		 * ComponentScan이란?
		 * base-package로 설정 된 하위 경로에 특정 어노테이션을 가지고 있는
         클래스를 이용하여 bean으로 등록한다.
		 * @Component 어노테이션이 작성 된 클래스를 인식하여 bean으로 만들게 되며
		 * 특수 목적에 따라 세부 기능을 제공하는 
         @Controller, @Service, @Repository, @Configuration 등을 인식한다.
		 * */
		
		ApplicationContext context 
        = new AnnotationConfigApplicationContext(ContextConfiguration1.class);
		
		String[] beanNames = context.getBeanDefinitionNames();
		for(String beanName : beanNames) {
			System.out.println("beanName : " + beanName);
		}
		
		MemberDAO memberDAO = context.getBean(MemberDAO.class);
		
		System.out.println(memberDAO.selectMember(1));
        
		System.out.println(memberDAO.insertMember(new MemberDTO(
        3, "user03", "pass03", "새로운멤버" )));
        
		System.out.println(memberDAO.selectMember(3));
		
		
	}

}

 

 

 

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.greedy.section01.javaconfig.config.ContextConfiguration2;

public class Application2 {

	public static void main(String[] args) {
		
		ApplicationContext context 
        = new AnnotationConfigApplicationContext(ContextConfiguration2.class);
		
		String[] beanNames = context.getBeanDefinitionNames();
		for(String beanName : beanNames) {
			System.out.println("beanName : " + beanName);
		}
		
		MemberDAO memberDAO = context.getBean(MemberDAO.class);
		
		System.out.println(memberDAO.selectMember(1));
		System.out.println(memberDAO.insertMember(new MemberDTO(
        3, "user03", "pass03", "새로운멤버")));
		System.out.println(memberDAO.selectMember(3));
		
		
	}

}

 

 

Application3

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.greedy.section01.javaconfig.config.ContextConfiguration3;

public class Application3 {

	public static void main(String[] args) {
		
		ApplicationContext context
        = new AnnotationConfigApplicationContext(ContextConfiguration3.class);
		
		String[] beanNames = context.getBeanDefinitionNames();
		for(String beanName : beanNames) {
			System.out.println("beanName : " + beanName);
		}
		
		MemberDAO memberDAO = context.getBean(MemberDAO.class);
		
		System.out.println(memberDAO.selectMember(1));
		System.out.println(memberDAO.insertMember(new MemberDTO(
        3, "user03", "pass03", "새로운멤버")));
		System.out.println(memberDAO.selectMember(3));

	}

}

 

 

 

interface MemberDAO

public interface MemberDAO {
	
	/* 회원 번호로 회원 정보를 조회하는 메소드 */
	MemberDTO selectMember(int sequence);

	/* 회원 정보를 저장하고 결과를 리턴하는 메소드 */
	boolean insertMember(MemberDTO newMember);
	
}

 

 

 

MemberDAOImpl implements MemberDAO

import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Repository;

/* 인터페이스로 메소드를 강제화 한 후 구현하여 사용하면 강제성이 부여 된다.
 * 또한 결합 관계를 느슨하게 만들 수 있다.
 * */

/* 스프링 컨테이너가 스캐닝 기능을 이용하여 해당 클래스를 bean으로 등록할
수 있는 어노테이션을 설정한다.
 * value 속성을 이용하면 bean의 id를 설정할 수 있으며, value는 생략 가능하다.
 * 이름(id)를 설정하지 않으면 Class의 앞 글자를 소문자로 하여 bean을 생성한다.
 * @Controller, @Service, @Repository와 동일한 기능을 가지지만 각 계층을 
 표현하는 어노테이션은
 * 특정 용도에 맞는 부가적인 혜택이 있으니 게층별로 구분하여 사용하는 것이 좋다.
 * */
 
//@Component(value="memberDAO")
@Repository(value="memberDAO")
public class MemberDAOImpl implements MemberDAO{
	
	private final Map<Integer, MemberDTO> memberMap;
	
	public MemberDAOImpl() {
		memberMap = new HashMap<>();
		
		memberMap.put(1, new MemberDTO(1, "user01", "pass01", "홍길동"));
		memberMap.put(2, new MemberDTO(2, "user02", "pass02", "유관순"));
	}

	/* 매개변수로 전달받은 회원 번호를 map에서 조회 후 회원 정보를 
    리턴해주는 용도의 메소드 */
	@Override
	public MemberDTO selectMember(int sequence) {
		return memberMap.get(sequence);
	}
	
	/* 매개변수로 전달받은 회원 정보를 map에 추가하고 성공 실패 여부를 
    boolean으로 리턴하는 메소드 */
	@Override
	public boolean insertMember(MemberDTO newMember) {
		
		int before = memberMap.size();
		
		memberMap.put(newMember.getSequence(), newMember);
		
		int after = memberMap.size();
		
		return (after > before) ? true : false;
	}

}

 

 

MemberDTO

public class MemberDTO {
	
	private int sequence;
	private String id;
	private String pwd;
	private String name;
	
	public MemberDTO() {}

	public MemberDTO(int sequence, String id, String pwd, String name) {
		super();
		this.sequence = sequence;
		this.id = id;
		this.pwd = pwd;
		this.name = name;
	}

	public int getSequence() {
		return sequence;
	}

	public void setSequence(int sequence) {
		this.sequence = sequence;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "MemberDTO [sequence=" 
        + sequence + ", id=" + id + ", pwd=" + pwd + ", name=" + name + "]";
	}
	
	

}

 

 

 

 

 

 

 


 

 

 

 

 

chap02-component-scan : section02

 

 

 

spring-context1.xml

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

	<!-- context 스키마가 있어야 정의할 수 잇으므로 하단의 namespaces 탭에서
    context 스키마를 추가하고 작성할 것 -->
	<context:component-scan base-package="com.greedy.section02.xmlconfig"/>
	
</beans>

 

 

spring-context2.xml

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

	<!-- context 스키마가 있어야 정의할 수 잇으므로 하단의 namespaces 탭에서
    context 스키마를 추가하고 작성할 것 -->
	<context:component-scan base-package="com.greedy.section02.xmlconfig">
		<context:exclude-filter type="assignable" 
        expression="com.greedy.section02.xmlconfig.MemberDAO"/>
	</context:component-scan>
	
</beans>

 

 

Application1

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Application1 {

	public static void main(String[] args) {
		
		ApplicationContext context
  = new GenericXmlApplicationContext("com/greedy/section02/xmlconfig/config/spring-context1.xml");
	
		String[] beanNames = context.getBeanDefinitionNames();
		for(String beanName : beanNames) {
			System.out.println("beanName : " + beanName);
		}
		
		MemberDAO memberDAO = context.getBean(MemberDAO.class);
		
		System.out.println(memberDAO.selectMember(1));
		System.out.println(memberDAO.insertMember(new MemberDTO(
        3, "user03", "pass03", "새로운멤버")));
		System.out.println(memberDAO.selectMember(3));
		
	
	}

}

 

 

Application2 

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class Application2 {

	public static void main(String[] args) {

		ApplicationContext context
= new GenericXmlApplicationContext("com/greedy/section02/xmlconfig/config/spring-context2.xml");
	
		String[] beanNames = context.getBeanDefinitionNames();
		for(String beanName : beanNames) {
			System.out.println("beanName : " + beanName);
		}
		
		MemberDAO memberDAO = context.getBean(MemberDAO.class);
		
		System.out.println(memberDAO.selectMember(1));
        
		System.out.println(memberDAO.insertMember(new MemberDTO(
        3, "user03", "pass03", "새로운멤버")));
        
		System.out.println(memberDAO.selectMember(3));
	}

}

 

 

MemberDAO 

public interface MemberDAO {
	
	/* 회원 번호로 회원 정보를 조회하는 메소드 */
	MemberDTO selectMember(int sequence);

	/* 회원 정보를 저장하고 결과를 리턴하는 메소드 */
	boolean insertMember(MemberDTO newMember);
	
}

 

 

 

MemberDAOImpl implements MemberDAO

import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Repository;

/* 인터페이스로 메소드를 강제화 한 후 구현하여 사용하면 강제성이 부여 된다.
 * 또한 결합 관계를 느슨하게 만들 수 있다.
 * */

/* 스프링 컨테이너가 스캐닝 기능을 이용하여 해당 클래스를 bean으로 등록할 수 있는 
어노테이션을 설정한다.
 * value 속성을 이용하면 bean의 id를 설정할 수 있으며, value는 생략 가능하다.
 * 이름(id)를 설정하지 않으면 Class의 앞 글자를 소문자로 하여 bean을 생성한다.
 * @Controller, @Service, @Repository와 동일한 기능을 가지지만 각 계층을
 표현하는 어노테이션은
 * 특정 용도에 맞는 부가적인 혜택이 있으니 게층별로 구분하여 사용하는 것이 좋다.
 * */
//@Component(value="memberDAO")
@Repository(value="memberDAO")
public class MemberDAOImpl implements MemberDAO{
	
	private final Map<Integer, MemberDTO> memberMap;
	
	public MemberDAOImpl() {
		memberMap = new HashMap<>();
		
		memberMap.put(1, new MemberDTO(1, "user01", "pass01", "홍길동"));
		memberMap.put(2, new MemberDTO(2, "user02", "pass02", "유관순"));
	}

	/* 매개변수로 전달받은 회원 번호를 map에서 조회 후 회원 정보를 
    리턴해주는 용도의 메소드 */
	@Override
	public MemberDTO selectMember(int sequence) {
		return memberMap.get(sequence);
	}
	
	/* 매개변수로 전달받은 회원 정보를 map에 추가하고 성공 실패 여부를 
    boolean으로 리턴하는 메소드 */
	@Override
	public boolean insertMember(MemberDTO newMember) {
		
		int before = memberMap.size();
		
		memberMap.put(newMember.getSequence(), newMember);
		
		int after = memberMap.size();
		
		return (after > before) ? true : false;
	}

}

 

 

 

MemberDTO

public class MemberDTO {
	
	private int sequence;
	private String id;
	private String pwd;
	private String name;
	
	public MemberDTO() {}

	public MemberDTO(int sequence, String id, String pwd, String name) {
		super();
		this.sequence = sequence;
		this.id = id;
		this.pwd = pwd;
		this.name = name;
	}

	public int getSequence() {
		return sequence;
	}

	public void setSequence(int sequence) {
		this.sequence = sequence;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "MemberDTO [sequence=" 
        + sequence + ", id=" + id + ", pwd=" + pwd + ", name=" + name + "]";
	}
	
	

}

 

 

 

 

 

 

 

 

'Programming > Spring Framework' 카테고리의 다른 글

5. Spring AOP (1)  (0) 2022.04.11
4. Spring DI 관리  (0) 2022.04.07
3. Spring IOC (1)  (0) 2022.04.06
2. Spring Framework  (0) 2022.04.06
1. Apache Maven  (0) 2022.04.06

+ Recent posts