[Spring] 스프링 입문 1

히똔 2022. 7. 20. 08:51
728x90
반응형

참고 강의 : (인프런) 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard

 

[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

 

 

@ResponseBody 객체 반환

@GetMapping("hello-string")
    @ResponseBody //http 바디에 직접 넣어주겠다. -> html 파일이 따로 필요없이 return 값이 걍 그 페이지 자체가 됨
    public String helloString(@RequestParam("name") String name){ // 링크에 ? 로 name 의 키값 입력해주는건 동일
        return "hello "+name; // 이거 그대로가 /hello-string 페이지에 보여짐
    }

@GetMapping("hello-api")
    @ResponseBody //viewResolver가 아닌 httpMessageConverter로 동작하고, 객체면 jsonConverter로 동작
    public Hello helloApi(@RequestParam("name") String name){
        Hello hello = new Hello();
        hello.setName(name); //json 방식으로 반환 => "name": "sprindfsd"
        return hello;
    }

    static class Hello{
        private String name;

        //getter setter 자동 생성성
        // java bin 표준 방식
        //property 접근 방식

       public String getName() {
            return name;
        }

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

@ResponseBody 를 사용하고, 객체를 반환하면 객체가 JSON으로 변환됨

실행

http://localhost:8080/hello-api?name=spring

@ResponseBody 사용 원리

  • @ResponseBody 를 사용
  • HTTP의 BODY에 문자 내용을 직접 반환
    viewResolver 대신에 HttpMessageConverter 가 동작
    기본 문자처리: StringHttpMessageConverter
    기본 객체처리: MappingJackson2HttpMessageConverter
    byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있음

참고: 클라이언트의 HTTP Accept 해더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해서
HttpMessageConverter 가 선택된다. 더 자세한 내용은 스프링 MVC 강의에서 설명하겠다

 

Test 케이스 작성

test 내 MemoryMemberRepositoryTest 클래스 만들고 작성

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.*;

class MemoryMemberRepositoryTest {
    MemoryMemberRepository repository = new MemoryMemberRepository();

    //Test 할때마다 레포를 깔끔히 지워줘야함
    @AfterEach
    public  void afterEach(){
        repository.clearStore();
    }

    @Test
    public void save(){
        Member member = new Member();
        member.setName("hehesun");

        repository.save(member);

        Member result = repository.findById(member.getId()).get();
        assertThat(member).isEqualTo(result);

    }

    @Test
    public void findByName(){
        //given
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        //when
        Member result = repository.findByName("spring1").get();
        //then
        assertThat(result).isEqualTo(member1);
    }
    @Test
    public void findAll() {
        //given
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        //when
        List<Member> result = repository.findAll();
        //then
        assertThat(result.size()).isEqualTo(2);
    }
}

main > repository > MemoryMemberRepository 에서 clearStore 메서드 추가

public void clearStore(){
        store.clear();
    }

 

회원 서비스 개발

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

import java.util.List;
import java.util.Optional;

public class MemberService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();

    /**
     * 회원가입
     */

    public Long join(Member member){
        validateDuplicateMember(member); //중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        //동명이인 안됨
        //null 일 가능성이 있으면 optional로 한번 감싸서 진행 => 자동으로 감싸짐
        memberRepository.findByName(member.getName())
                .ifPresent(m -> {
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });
    }

    /**
     * 전체 회원 조회
     */
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }
    public Optional<Member> findOne(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

 

회원 서비스 테스트

ctrl + shift + t ⇒ test case 자동 작성

given when then 문법

  • given : 주어진 상황
  • when : 이걸로 실행되었을때
  • then : 이렇게 결과가 나와야함 (검증부)

기존에는 회원 서비스가 메모리 회원 리포지토리를 직접 생성하게 했다

public class MemberService {
 private final MemberRepository memberRepository = new MemoryMemberRepository();
}

회원 리포지토리의 코드가

회원 서비스 코드를 DI 가능하게 변경한다.

public class MemberService {
    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository; //외부에서 넣어주도록 바꾸기
    }

회원 서비스 테스트

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.*;

class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach
    public void beforeEach() {
        //테스트가 독립적으로 실행되야기 때문에 각각 객체 생성
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }

    @AfterEach
    public void afterEach() {
        memberRepository.clearStore();
    }

    @Test
    void join() {
        //회원가임

        //Given
        Member member = new Member();
        member.setName("spring");

        //When
        Long saveId = memberService.join(member);

        //Then
        Member findMember = memberService.findOne(saveId).get();
        assertEquals(member.getName(), findMember.getName());
    }

    @Test
    public void 중복_회원_예외() {
        //Given
        Member member1 = new Member();
        member1.setName("spring");

        Member member2 = new Member();
        member2.setName("spring");

        //When
        memberService.join(member1);
        IllegalStateException e = assertThrows(IllegalStateException.class,
                () -> memberService.join(member2));//예외가 발생해야 한다.
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
    }

    @Test
    void findMembers() {
    }

    @Test
    void findOne() {
    }
}
  • @BeforeEach : 각 테스트 실행 전에 호출된다. 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고, 의존관계도 새로 맺어준다.

 

스프링 빈을 등록하는 2가지 방법

  1. 컴포넌트 스캔과 자동 의존관계 설정
  2. 자바 코드로 직접 스프링 빈 등록하기

1. 컴포넌트 스캔과 자동 의존관계 설정

컴포넌트 스캔 원리

  • @Component 애노테이션이 있으면 스프링 빈으로 자동 등록된다.
  • @Controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문이다.
  • @Component 를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록된다.
    • @Controller
    • @Service
    • @Repository

2. 자바 코드로 직접 스프링 빈 등록하기

원 서비스와 회원 리포지토리의 @Service, @Repository, @Autowired 애노테이션을 제거하고
진행한다. (Controller에 @Controller만 남겨둠)

package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {

 @Bean
 public MemberService memberService() {
 return new MemberService(memberRepository());
 }

 @Bean
 public MemberRepository memberRepository() {
return new MemoryMemberRepository();
 }

}

 

스프링 IoC 컨테이너와 빈

https://www.youtube.com/watch?v=L-0UvbFUXrk (백기선님 강의 일부)

Inversion of Control : 의존성 관계 주입 (Dependency Injection) 이라고도 함. 어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는 것이 아니라, 주입 받아 사용하는 방법.

스프링 IoC 컨테이너

  • 빈들이 모여있음 (코드로 모아놔야함)
  • 애플레케이션 컴포넌트의 중앙 저장소
  • 빈 설정 소스로 부터 빈 정의를 읽어들이고, 빈을 구성하고 제공한다

  • 스프링 IoC 컨테이너가 관리하는 객체.
  • 의존성 주입을 받으려면 빈으로 들어가 있어야함
  • 기본적으로 싱글톤 스코프로 관리된다.

어노테이션 Service, Repository, Autowired 를 통해 빈으로 주입할 수 있다.

728x90
반응형