스프링 

 

스프링 이해 예제

 

1. 인사말 ( 콘솔에 간단한 메시지를 출력하는 자바 클래스 )

package chap02;

// 생성
public class Greeter {
	// 멤버 변수 선언 - 문자열 형식을 저장하는 format 변수
	// 문자열 형식이란, 예를 들어 "Hello, %s!"와 같이 %s 부분에 실제 이름이 들어가는 패턴을 말합니다.
	private String format;
	
	 // 인자로 전달된 guest를 포맷에 맞추어 인사말을 생성하는 메서드
	public String greet(String guest) {
	// String.format 메서드를 사용하여 format 문자열에 guest를 삽입하여 최종 인사말 생성 후 반환
	// guest는 파라미터(입력값)	
		return String.format(format, guest);
	// String.format() 메서드를 사용하여 format 변수에 저장된 문자열 형식에 guest를 삽입하여 최종 인사말을 생성한 후 반환	
	}
	
	// format 멤버 변수를 설정하는 메서드
	// 외부에서 format 변수의 내용을 변경할 수 있음
	
	public void setFormat(String format) {
		this.format = format;
	}
	
 // 외부에서 해당 처럼 호출 가능	
//	// Greeter 객체 생성
//	Greeter greeter = new Greeter();
//
//	// 인사말 포맷 설정
//	greeter.setFormat("Hello, %s!");
//
//	// greet 메서드를 사용하여 인사말 생성
//	String greetingMessage = greeter.greet("John");
//	System.out.println(greetingMessage); // 출력 결과: "Hello, John!"

}

 

 

2. 스프링 설정 파일

package chap02;

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

// 등록

@Configuration 
// Spring의 설정 클래스임을 나타내는 어노테이션
// Spring은 이 클래스를 참조하여 빈 등록과 의존성 주입을 처리
public class AppContext {

	// 1. @Bean 어노테이션이 붙은 메서드는 Spring 컨텍스트에 빈으로 등록
	@Bean
	public Greeter greeter() {
		// 2. Greeter 클래스의 인스턴스를 생성합니다.
		Greeter g = new Greeter();
		// 3. 인사 메시지의 형식을 설정합니다. "%s, 안녕하세요!"는 후에 사용자의 이름이 들어갈 위치를 나타낸다.
		g.setFormat("%s, 안녕하세요!");
		return g; // 설정이 완료된 Greeter 인스턴스를 반환
	}

}

위의 코드를 사용하면 Spring 컨테이너가 Greeter 클래스의 인스턴스를 생성하고, 인사 메시지의 형식을 설정한 뒤,

해당 빈을 필요한 곳에서 주입하여 사용할 수 있게 된다.

 

 

3. 스프링이 제공하는 클래스를 이용해서 읽어와 사용하는 코드 

package chap02;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main2 {

	public static void main(String[] args) {
		
		// 1. Spring 컨텍스트를 생성합니다. AppContext 클래스를 기반으로 새로운 컨텍스트를 만듭니다.
		        // // Spring 컨텍스트 초기화
		AnnotationConfigApplicationContext ctx = 
				new AnnotationConfigApplicationContext(AppContext.class);
		
		// 2. 컨텍스트에서 "greeter" 빈을 가져옵니다. AppContext 클래스에서 빈으로 등록한 Greeter 클래스의 인스턴스를 반환
		 // String 컨테이너에서 Greeter 빈을 가져옴
		Greeter g = ctx.getBean("greeter", Greeter.class);
		
		 // 3. Greeter 클래스의 greet 메서드를 호출하여 인사 메시지를 생성합니다. "스프링"을 인자로 주어서 "Greeter" 빈에 설정된 형식으로 인사 메시지를 생성
		String msg = g.greet("스프링");
		
		System.out.println(msg);
		
		 // 5. Spring 컨텍스트를 닫습니다.
		ctx.close();
	}
}

 

 

4. 결과

 

 

 

스프링은 객체 컨테이너 

스프링의 핵심 기능은 객체를 생성하고 초기화 하는 것이다.

이와 관련된 기능은 ApplicationContext라는 인터페이스 정의 되어 있다. ( 위에 이미지 참조 )

위에 실행 코드에 AnnotationConfigApplicationContext 클래스는 이 인터페이스를 알맞게 구현한 클래스 중 하나

자바클래스에서 정보를 읽어와 객체 생성과 초기화를 수행 ( 개발자가 객체 생성과 초기화를 안해도 됨 ) 

 

BeanFactory 인터페이스는 객체 생성과 검색에 대한 기능을 정의 이외의 싱글톤, 프로토타입 빈인지 확인하는 기능도 제공

 

어떤 구현 클래스를 사용하든, 각 구현 클래스는 설정 정보로부터 빈(Bean)이라고 불리는 객체를 생성하고 그 객체를 내부에 보관한다. ( 객체를 서비스 ) 

 

 

싱글톤 객체

 

주소 값 같음 ( @Bean 1개 ) 

package chap02;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
	// 싱글톤
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = 
				new AnnotationConfigApplicationContext(AppContext.class);
		// 주소 값 비교
		// 주소 값은 같고 변수만 다름 
		Greeter g1 = ctx.getBean("greeter", Greeter.class);
		Greeter g2 = ctx.getBean("greeter", Greeter.class);
		System.out.println("(g1 == g2) = " + (g1 == g2));
		ctx.close();
	}
}

 

주소 값 다름 ( @Bean 2개 )

 

package chap02;

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

// 등록

@Configuration
public class AppContext {
	// 1. @Bean 어노테이션이 붙은 메서드는 Spring 컨텍스트에 빈으로 등록
	@Bean
	public Greeter greeter() {
		// 2. Greeter 클래스의 인스턴스를 생성합니다.
		Greeter g = new Greeter();
		// 3. 인사 메시지의 형식을 설정합니다. "%s, 안녕하세요!"는 후에 사용자의 이름이 들어갈 위치를 나타냅니다.
		g.setFormat("%s, 안녕하세요!");
		return g;
	}
	
	@Bean
	public Greeter greeter2() {
		// 2. Greeter 클래스의 인스턴스를 생성합니다.
		Greeter g = new Greeter();
		// 3. 인사 메시지의 형식을 설정합니다. "%s, 안녕하세요!"는 후에 사용자의 이름이 들어갈 위치를 나타냅니다.
		g.setFormat("%s, 안녕하세요!");
		return g;
	}

}

 

package chap02;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
	// 싱글톤
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = 
				new AnnotationConfigApplicationContext(AppContext.class);
		// 주소 값 비교
		// 주소 값은 같고 변수만 다름 
		Greeter g1 = ctx.getBean("greeter", Greeter.class);
		Greeter g2 = ctx.getBean("greeter2", Greeter.class);
		System.out.println("(g1 == g2) = " + (g1 == g2));
		ctx.close();
	}
}

 

스프링 DI ( Dependency Injection )

DI는 우리말로는 '의존 주입'이라고 번역한다. 의존은 객체 간의 의존을 의미한다. 

한 클래스가 다른 클래스의 메서드를 실행할 때 이를 '의존'한다고 표현

'의존'은 변경에 의해 영향을 받는 관계를 의미

 

 

의존 관계, 의존 처리 예제

 

Dao 클래스 

package spring;

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

public class MemberDao {

	private static long nextId = 0;

	private Map<String, Member> map = new HashMap<>();

	public Member selectByEmail(String email) {
		return map.get(email);
	}

	public void insert(Member member) {
		member.setId(++nextId);
		map.put(member.getEmail(), member);
	}

	public void update(Member member) {
		map.put(member.getEmail(), member);
	}

	public Collection<Member> selectAll() {
		return map.values();
	}
}

 

 

의존 관계 

package spring;

import java.time.LocalDateTime;

public class MemberRegisterService {

	private MemberDao memberDao = new MemberDao();	
	
	// 회원 등록을 수행하는 메서드
	public Long regist(RegisterRequest req) {
		// 입력한 이메일로 이미 등록된 회원이 있는지 조회
		Member member = memberDao.selectByEmail(req.getEmail());
		// 같은 이메일을 가진 회원이 이미 존재하면 익셉션 발생
		if (member != null) {
			throw new DuplicateMemberException("dup email " + req.getEmail());
		}
		// 같은 이메일을 가진 회원이 존재하지 않으면 DB에 삽입 
		Member newMember = new Member(
				req.getEmail(), req.getPassword(), req.getName(), 
				LocalDateTime.now());
		memberDao.insert(newMember);
		
		// 새로 등록한 회원의 ID를 반환
		return newMember.getId();
	}
}

 

MemberRegisterService 클래스가 MemberDao 클래스에 의존 

의존하게 되면 MemberDao클래스의 insert() 메서드의 이름을 insertMember()로 변경하면 이 메서드를 사용하는 MemberRegisterService 클래스의 소스 코드도 함께 변경 된다. 이렇게 변경에 따른 영향이 전파되는 관계를 의존한다고 표현 한다. 클래스 내부에서 직접 의존 객체를 생성하는 것이 쉽긴 하나 유지보수 관점에서 문제점을 유발할 수 있다.

 

 

 

 

 

 

위에 코드 DI를 통한 의존 처리

직접 의존 객체를 생성했던 위에 코드와 달리 밑에 코드는 의존 객체를 직접 생성하지 않고 생성자를 통해서 의존객체를

전달 받는다. 즉 생성자를 통해 MemberRegisterService가 의존하고 있는  MemberDao 객체를 주입 받은 것이다.

 

DI를 하게 되면 코드가 더 길어지지만 사용하는 이유는 변경의 유연함이 있기 때문이다.

 

	private MemberDao memberDao;
	
	// 생성자를 통해 MemberDao 객체를 주입받음
	// 의존 객체를 생성자를 통해 주입한다.
	public MemberRegisterService(MemberDao memberDao) {
		this.memberDao = memberDao;
	}

 

package spring;

import java.time.LocalDateTime;

public class MemberRegisterService {
	private MemberDao memberDao;
	
	// 생성자를 통해 MemberDao 객체를 주입받음
	// 의존 객체를 생성자를 통해 주입한다.
	public MemberRegisterService(MemberDao memberDao) {
		this.memberDao = memberDao;
	}
	// 회원 등록을 수행하는 메서드
	public Long regist(RegisterRequest req) {
		// 입력한 이메일로 이미 등록된 회원이 있는지 조회
		Member member = memberDao.selectByEmail(req.getEmail());
		// 같은 이메일을 가진 회원이 이미 존재하면 익셉션 발생
		if (member != null) {
			throw new DuplicateMemberException("dup email " + req.getEmail());
		}
		// 같은 이메일을 가진 회원이 존재하지 않으면 DB에 삽입 
		Member newMember = new Member(
				req.getEmail(), req.getPassword(), req.getName(), 
				LocalDateTime.now());
		memberDao.insert(newMember);
		
		// 새로 등록한 회원의 ID를 반환
		return newMember.getId();
	}
}

 

 

 

DI와 의존 객체 변경의 유연함

예를 들어서 회원등록 기능이 가능한 클래스에 의존 객체를 생성하게 됫는데 암호변경 기능이 필요해서 암호 변경 클래스를 만들어서 똑같이 의존 객체를 생성하게 됫고 이상태에서 빠른 조회를 위해 캐시를 적용해야 하는 상황에서 의존 객체 클래스를 상속받은 캐시 클래스 생성하게 되면 회원등록 클래스랑 암호변경 클래스를 밑에 이미지 처럼  둘 다 변경해야 된다.

 

 

동일한 상황에서 DI를 사용해서 생성자를 통해서 의존 객체를 주입 받도록 구현하면 수정할 코드가 줄어든다. 

이렇게 DI를 사용하면 MemberDao 객체를 사용하는 클래스가 세 개여도 변경할 곳은 의존 주입 대상이 되는 객체를

생성하는 코드 한 곳뿐이다. 의존객체를 직접 생성했던 방식에 비해 변경할 코드가 한 곳으로 집중되는 것을 알 수 있다.

 

 

 

 

오라클

 

트랜잭션 제어와 세션

하나의 단위로 데이터를 처리하는 트랜잭션

트랜잭션이란 더 이상 분할할 수 없는 최소 수행 단위를 뜻하며 계좌 이체와 같이 하나의 작업 또는 밀접하게 연관된 작업을 수행하기 위해 한 개 이상의 데이터 조작 명령어(DML)로 이루어진다. 즉 어떤 기능 한 가지를 수행하는 'SQL문 덩어리' 라고 볼 수 있다.

트랜잭션은 하나의 트랜잭션 내에 있는 여러 명령어를 한 번에 수행하여 작업을 완료하거나 아예 모두 수행하지 않는 상태, 즉 모든 작업을 취소한다. 이러한 특성으로 트랜잭션 의미를 'ALL OR NOTHING'문장으로 설명 하기도 한다. 그리고 트랜잭션을 제어하기 위해 사용하는 명령어를 TCL이라고 한다.

트랜잭션은 데이터베이스 계정을 통해 접속하는 동시에 시작 된다.

 

 

트랜잭션 예제

 

 

DEPT_TCL 테이블에 데이터를 입력 수정 삭제 하기 

insert into dept_tcl values(50, 'DTABASE', 'SEOUL');
update dept_tcl set loc = 'BUSAN' where deptno = 40;
delete from dept_tcl where dname = 'RESEARCH';
select * from dept_tcl;

 

트랜잭션을 취소하고 싶을 때 ROLLBACK

ROLLBACK;

select * from dept_tcl;

 

 

 

트랜잭션을 영원히 반영하고 싶을 때 COMMIT 

insert into dept_tcl values(50, 'DTABASE', 'SEOUL');

테스트를 위해 데이터 추가

 

( COMMIT 후에는 ROLLBACK 안됨 ) 

ROLLBACK;

select *
 from dept_tcl

 

'프로젝트 기반 자바(JAVA) 응용 SW개발자 취업과정' 카테고리의 다른 글

2023-07-21 46일차  (0) 2023.07.21
2023-07-20 45일차  (0) 2023.07.20
2023-07-18 43일차  (0) 2023.07.18
2023-07-17 42일차  (0) 2023.07.17
2023-07-14 41일차  (0) 2023.07.14

+ Recent posts