스프링 프레임워크란?

예지성준·2024년 7월 4일

스프링프레임워크

목록 보기
1/14
post-thumbnail

1. 스프링 프레임워크란?(Spring Framework)

spring core api doc

spring6
jdk17

Framework

틀 제공/ 틀이 정해져있다. 이미 구현되어있는 편의 기능을 제공해준다.

1) 의존 주입(Dependency Inject : DI) 지원

  • 특정 기능이 특정 객체 메서드 내에서 다른 객체를 필요로 할 때!
  • 다른 객체와 협동을 통해 만들어짐
  • 자동 의존성 주입
  • 통제가 가능해진다 (내부에 있으면 통제 x) 열어놓게 되면 필요한 객체만 주입해도 자동 조립이 된다.

기존 jsp에선 객체 조립기를 이용해서 의존성을 직접 주입해줬다.

2) AOP(Aspect-Oriented Programming) 지원

  • 관점 지향 프로그래밍/ 관점 - 개발자의 공통적인 처리 부분
  • 📚프록시(proxy) : 대신하다, 대리하다.

개발하기 편리한 틀을 제공해줌

3) MVC 웹 프레임워크 제공

  • spring-webmvc : 스프링에서 제공하는 틀

4) JDBC, JPA 연동, 선언적 트랜잭션 처리 등 DB 연동 지원

JPA(Java Persistence API - ORM 표준 설계)

5) 스프링 데이터, 스프링 시큐리티, 스프링 배치

스프링 데이터 - 데이터 처리를 추상화 시켜둠

스프링 시큐리티 - 인증(로그인), 인가(페이지 통제)

스프링 배치 - 콘솔, 일괄처리 지원

🔹스프링 데이터 : 적은 양의 코드로 데이터 연동을 처리할 수 있도록 도와주는 프레임워크이다. JPA, 몽고DB, 레디스 등 다양한 저장소 기술을 지원한다.

🔹스프링 시큐리티 : 인증/인가와 관련된 프레임워크로서 웹 접근 제어, 객체 접근 제어, DB - 오픈 ID - LDAP 등 다양한 인증 방식, 암호화 기능을 제공한다.

🔹스프링 배치 : 로깅/추적, 작업 통계, 실패 처리 등 배치 처리에 필요한 기본 기능을 제공한다.

2. 스프링 프로젝트 생성하기

spring-context -> 의존성

maven

1) maven 프로젝트 생성

2) maven repository에서 의존성 찾아서 pom.xml내의 dependencies에 추가

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.1.10</version>
    </dependency>

gradle🐘

spring6 - JDK 컴파일 버전 17

추가 할 의존성은 maven과 같이 Spring Context를 추가하면 된다.

build.gradle에 추가 + UTF8, 인텔리제이에 탑재된 Gradle로 세팅 변경

implementation 'org.springframework:spring-context:6.1.10'

3. 스프링은 객체 컨테이너

  • 스프링의 핵심 기능은 객체를 생성하고 초기화 하는 것이다. 이와 관련된 기능은
    ApplicationContext라는 인터페이스에 정의되어 있다.

  • AnnotationConfigApplicationContext 클래스는 이 인터페이스를 알맞게 구현한 클래스 중 하나이다. AnnotationConfigApplicationContext 클래스는 자바 클래스에서 정보를 읽어와 객체 생성과 초기화를 수행한다.

  • XML 파일이나 그루비 설정 코드를 이용해서 객체 생성/초기화를 수행하는 클래스도 존재한다.


🔸AnnotationConfigApplicationContext 클래스 계층도 일부

🔼 BeanFactory / ApplicationContext: 스프링 컨테이너

ApplicationContext(또는 BeanFactory)는 빈 객체의 생성, 초기화, 보관, 제거 등을 관리하고 있어 ApplicationContext를 컨테이너(Container)라고도 부른다.

  • BeanFactory 인터페이스는 객체 생성과 검색에 대한 기능을 정의한다. 예를 들어 생성된 객체를 검색하는데 필요한 getBean() 메서드가 BeanFactory에 정의되어 있다. 객체를 검색하는 것 이외에 싱글톤/프로토타입 빈인지 확인하는 기능도 제공한다.

  • ApplicationContext 인터페이스는 메시지, 프로필/환경 변수 등을 처리할 수 있는 기능을 추가로 정의한다.

  • AnnotationConfigApplicationContext를 비롯해 계층도의 가장 하단에 위치한 세개의 클래스는 BeanFactory와 ApplicationContext에 정의된 기능의 구현을 제공한다.

    • AnnotationConfigApplicationContext : 자바 애노테이션을 이용한 클래스로부터 객체 설정 정보를 가져온다.
      🔼 이걸 주로 사용하게 될 것!
    • GenericXmlApplicationContext : XML로 부터 객체 정보를 가져온다.
    • GenericGroovyApplicationContext : 그루비 코드를 이용해 설정 정보를 가져온다.

🔼 가변인자.. 설정 클래스 여러개를 넣을 수 있다. 패키지를 지정하는 것도 가능


◾ 어떤 구현 클래스를 사용하든, 각 구현 클래스는 설정 정보로부터 빈(Bean)이라고 불리는 객체를 생성하고 그 객체를 내부에 보관한다. 그리고 getBean() 메서드를 실행하면 해당하는 빈 객체를 제공한다.

public class Greeter {
    public void hello(String name){
        System.out.println("Hello "+name+"!!!");
    }
}

//AppCtx.java 설정클래스
//설정 클래스 - 스프링 컨테이너가 관리할 객체를 정의하고 설정하는 역할
@Configuration
public class AppCtx {
    
    @Bean //스프링 컨테이너가 관리 해야할 객체임을 알려주는 역할(Bean)
    public Greeter greeter(){
        return new Greeter();
    }
}

//Ex01
public class Ex01 {
    @Test
    void test1(){
        //스프링 컨테이너 객체 생성
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppCtx.class);
        //매개변수에 스프링이 관리해야할 설정 클래스를 알려준다.
        
        //위 컨테이너 안에 현재 Greeter객체가 담겨있다.

        Greeter greet = ctx.getBean("greeter", Greeter.class);
        greet.hello("성준");

        ctx.close();
    }
}
//hello 성준 출력!

IoC

  • Inversion Of Control : 제어의 역전
  • 개발자가 해야되는 객체의 관리 -> 스프링 컨테이너가 대신 수행
  • 다양한 방식으로 객체 관리

1) 싱글톤 방식으로 객체를 관리함(동일 객체)

동일 객체가 greet과 greet2에 대입된다!

◾ 별도 설정을 하지 않을 경우 스프링은 한 개의 빈 객체만을 생성하며, 이 때 빈 객체는 "싱글톤(singleton) 범위를 갖는다"고 표현한다.
싱글톤은 단일 객체(single object)를 의미하는 단어로 스프링은 기본적으로 한 개의 @Bean 애노테이션에 대해 한 개의 빈 객체를 생성한다.

싱글톤으로 기본 관리하므로 객체가 한개만 있는 경우가 많다. 그러므로 Class 클래스만 있어도 찾는데 문제 없음 ‼🙆‍♀️
결론! Greeter 타입의 빈이 하나만 존재하면 이름을 명시하지 않고도 타입만으로 빈을 조회할 수 있다.

4. 스프링 DI(Dependency Injection - 의존주입)

1) 의존(Dependency)

  • 의존이란 객체간의 의존을 의미한다.
  • 협동, 상호작용
  • 필요로 하는 객체 - 의존성

2) DI를 통한 의존 처리

  1. DI 방식1 : 생성자 방식
package spring;

import java.time.LocalDateTime;

public class MemberRegisterService {
	private 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());
		}
		Member newMember = new Member(req.getEmail(), req.getPassword(), req.getName(), LocalDateTime.now());
		memberDao.insert(newMember);
		return newMember.getId();
	}
}
  • 생성자를 통해 MemberRegisterService가 의존(Dependency)하고 있는 MemberDao 객체를 주입(Injection) 받음
  • 의존 객체를 직접 구하지 않고 생성자를 통해서 전달받기 때문에 이 코드는 DI(의존 주입) 패턴을 따르고 있다.
  1. DI 방식2 : 세터 메서드 방식
package spring;

public class ChangePasswordService {

	private MemberDao memberDao;

	public void changePassword(String email, String oldPwd, String newPwd) {
		Member member = memberDao.selectByEmail(email);
		if (member == null)
			throw new MemberNotFoundException();

		member.changePassword(oldPwd, newPwd);

		memberDao.update(member);
	}

	public void setMemberDao(MemberDao memberDao) {
		this.memberDao = memberDao;
	}

}
  • setMemberDao() 메서드로 의존하는 MemberDao를 전달받는다. 즉 세터(setter)를 통해서 의존 객체를 주입받는다.
  1. @Configuration

  2. @Bean

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

➰로그인 & 회원가입 ➰

  • 기본적으로 검증, 기능(서비스), 컨트롤러(데이터 전달 클래스), 개체 담을 패키지 구성
  • 공통기능(global) 담을 검증 패키지에 인터페이스 제네릭 타입으로 생성
    • 각 validator에 해당 인터페이스 implements 하고 메서드 오버라이드
  • 편의를 위해 lombok 의존성도 넣어주기
  • 개체 Member클래스

※SOLID
S - Single Responsibility Principal

  • 단일 책임 원칙
    하나의 클래스는 하나의 역할을 담당
  • DAO: 데이터 영구 저장
    • MemberDao클래스
#MemberDao클래스
public class MemberDao {

    /*
    DB에 저장하지 않고 임시 메모리에 저장
     */
    private static Map<String, Member> members = new HashMap<>();

    public void register(Member member) {
      members.put(member.getEmail(),member);
    }

    public Member get(String email){
        return members.get(email);
    }
    public List<Member> getList(){
        List<Member> data = new ArrayList<>(members.values());
        return data;
    }
}
#JoinService클래스
public class JoinService {
    private JoinValidator validator;
    private MemberDao memberDao;

    /*
    //연관 관계 - 없어도 객체 생성 가능
    //setter을 통한 주입
    public void setValidator (JoinValidator validator) {
        this.validator = validator;
    }
    */

    //의존관계 - 없으면 객체 생성 불가
    //생성자를 통한 주입
    public JoinService(JoinValidator validator, MemberDao memberDao) {
        this.validator = validator;
        this.memberDao = memberDao;
    }

    public void process(RequestJoin form){
        validator.check(form); //joinService는 validator 객체와 form 객체를 의존하고있다.
        // -> 의존성!!

        //데이터 영구 저장 - DAO(Data Access Object)
        Member member = Member.builder()
                        .email(form.getEmail())
                        .password(form.getPassword())
                        .userName(form.getUserName())
                        .regDt(LocalDateTime.now())
                        .build();
        memberDao.register(member);
    }
}
  • 한 클래스가 다른 클래스의 메서드를 실행 할 때 이를 의존한다고 표현
  • joinservice가 JoinValidator와 MemberDao클래스에 의존한다.

5. 객체 조립기

객체 조립기에 필요한 의존성을 추가해주기만 하면 해당 의존성을 필요로 하는 곳에서 손 쉽게 반영될 수 있다.

#Assembler클래스
public class Assembler {
    public MemberDao memberDao(){
        return new MemberDao();
    }

    public JoinValidator joinValidator(){
        return new JoinValidator();
    }
    public JoinService joinService(){
        return new JoinService(joinValidator(),memberDao());
    }
}

객체 조립기 사용🤹‍♂️

객체 조립기 내부에서 객체를 생성해준다!


🎀스프링이 대신해주는 객체 조립 시간 ~!

위에서 사용한 Assembler(객체 조립기 클래스)가 필요 없어진다.

#스프링 컨테이너가 관리할 객체를 정의하고 설정 하는 클래스 AppCtx2
@Configuration
public class AppCtx2 {

    @Bean
    public MemberDao memberDao(){
        return new MemberDao();
    }

    @Bean
    public JoinValidator joinValidator(){
        JoinValidator joinValidator = new JoinValidator();
        joinValidator.setMemberDao(memberDao());

        return joinValidator;
    }

    @Bean
    public JoinService joinService(){
        return new JoinService(joinValidator(),memberDao());
    }
}
# 컨테이너 객체 생성
public class Ex03 {

    @Test
    void test1(){
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppCtx2.class);

        JoinService joinService = ctx.getBean(JoinService.class);
        MemberDao memberDao = ctx.getBean(MemberDao.class);
        
        RequestJoin form = RequestJoin.builder()
                .email("user01@test.org")
                .userName("사용자01")
                .password("12345678")
                .confirmPassword("12345678")
                .build();
        
        joinService.process(form);
        
        memberDao.getList().forEach(System.out::println);
        
        ctx.close();
    }
}

  • 회원정보를 조회할 수 있는 서비스 추가(InfoService)
    • memberDao로 직접 접근하는 것을 막기위함
      main 메서드에서 의존 대상 객체를 생성하고 주입하는 방법이 나쁘진 않지만 이 방법보다 더 나은 방법은 객체를 생성하고 의존 객체를 주입해 주는 클래스는 따로 작성하는 것이다.

설정 클래스에 Bean객체로 추가

똑같은 동작하는 걸 확인했다

📢📢하지만 객체가 생길때마다 @Bean으로 설정해서 넣어주게 되는것도 번거로운 일이 될수 있다. 설정해야할 객체가 100개라면 Bean객체 선언을 100개를 선언해야할 것..

profile
꽁꽁 얼어붙은 한강 위로 😺

0개의 댓글