Post

Repository, Service, Test, springBean, component scan

๐Ÿ“Œ Summary

  • โœ”๏ธ Optional
  • if there is possibility to be null, Optional and use ifPresent

  • โœ”๏ธ Test code:
  • given, when, then
  • Asssertions.assertThat()
  • for exceptions, use Assertions.assertThrows()
  • @AfterEach() to clear data
  • @BeforeEach() to inject repository in service โžก๏ธ dependency injection

  • โœ”๏ธ Component scan, spring bean
  • @Component์„ ๋ถ™์ด๋ฉด spring bean์œผ๋กœ ์ž๋™ ๋“ฑ๋ก๋จ
  • spring bean์œผ๋กœ ๋“ฑ๋ก๋˜๋ฉด: spring container๊ฐ€ dependency inject
  • @Component๋Š” ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค์–ด spring container๊ฐ€ ๊ด€๋ฆฌ โžก๏ธ singleton(ํ•˜๋‚˜๋งŒ uniqueํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค)
  • @Autowired๋ฅผ ์จ์„œ spring bean์„ ์ž๋™์œผ๋กœ inject dependency, ์ด์ œ ์„œ๋น„์Šค๋Š” repository๋ฅผ ์“ธ ์ˆ˜ ์žˆ๊ณ  controller๋Š” ์„œ๋น„์Šค๋ฅผ ๊ฐ€์ ธ๋‹ค ์“ธ ์ˆ˜ ์žˆ์Œ

โœ… Repository

  • Member Repository is interface
  • Memory Member Repository implements Member Repository
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//interface MemberRepository
public interface MemberRepository {
    Member save(Member member);
    Optional<Member> findById(Long id);
    Optional<Member> findByName(String name);
    List<Member> findAll();
}

//MemoryMemberRepository
public class MemoryMemberRepository implements MemberRepository{
    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L;

    @Override
    public Member save(Member member) {
    }

    @Override
    public Optional<Member> findById(Long id) {
    //optional: to handle if return value is null
    }

    @Override
    public Optional<Member> findByName(String name) {
    }

    @Override
    public List<Member> findAll() {
    }

    public void clearStore(){ //to clear repository after test
        store.clear();
    }
}

โœ… Repository Test

  • โญ๏ธ After each
  • ํ…Œ์ŠคํŠธ ์‹คํ–‰ ํ›„ ๊ณต์œ  ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ฆฌ(clear)ํ•˜๋Š” ์ฝ”๋“œ
  • MemoryMemberRepositoryTest test code์•ˆ์—๋Š” 3๊ฐœ์˜ ํ…Œ์ŠคํŠธ๊ฐ€ ๋“ค์–ด์žˆ์Œ
  • ์ด 3๊ฐœ์˜ ํ…Œ์ŠคํŠธ๋Š” ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ์ด ์•ˆ ๋จ
  • ๋”ฐ๋ผ์„œ ํ•œ ๊ฐœ์˜ ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด repository๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด ์ฃผ์–ด์•ผ ํ•จ โžก๏ธ afterEach()

  • โญ๏ธ Assertions.assertThat()์„ ์‚ฌ์šฉํ•ด ๊ฒฐ๊ณผ ๋น„๊ต
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class MemoryMemberRepositoryTest {
    MemoryMemberRepository respository = new MemoryMemberRepository();

    @AfterEach
    public void afterEach(){
        respository.clearStore();
        //ํ…Œ์ŠคํŠธ๋Š” ์ˆœ์„œ ๋ณด์žฅ์ด ์•ˆ ๋จ
        //ํ…Œ์ŠคํŠธ ํ•˜๋‚˜ ๋๋‚  ๋•Œ๋งˆ๋‹ค ์ดˆ๊ธฐํ™”
    }
    @Test
    public void save(){
        Member member = new Member();
        member.setName("spring");

        respository.save(member);
        Member result = respository.findById(member.getId()).get();
        assertThat(member).isEqualTo(result);
    }
    @Test
    public void findByName(){
        Member member1 = new Member();
        member1.setName("spring1");
        respository.save(member1);

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

        Member result = respository.findByName("spring1").get();
        assertThat(result).isEqualTo(member1);
    }

    @Test
    public void findAll(){
        Member member1 = new Member();
        member1.setName("spring1");
        respository.save(member1);

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

        List<Member> result = respository.findAll();
        assertThat(result.size()).isEqualTo(2);
    }
}

โœ… Member Service

  • โญ๏ธ Optional
  • Optional: null์ด ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๊ฒƒ์„ ๊ฐ์‹ผ๋‹ค
    • ๋ฉค๋ฒ„๋ฅผ name์œผ๋กœ ์ฐพ์„ ๋•Œ, ์—†์„ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ(null) Optional๋กœ ๊ฐ์‹ผ๋‹ค
  • Optional์„ ์‚ฌ์šฉํ•˜๋ฉด isPresent๋ฅผ ์“ธ ์ˆ˜ ์žˆ์Œ
  • ๊ทธ๋Ÿฌ๋ฉด null์ผ ๋•Œ ~๊ฒŒ ํ•˜๋ผ๊ณ  ์‰ฝ๊ฒŒ ๋˜์งˆ ์ˆ˜ ์žˆ๋‹ค
  • ๐Ÿ‘Ž๐Ÿป ๊ธฐ์กด์—๋Š” ๋ฉค๋ฒ„๋ฅผ ์ฐพ์•„์™€ if (member == null) ์„ ํ–ˆ์–ด์•ผ ํ•จ
  • ๐Ÿ‘๐Ÿป Optional์„ ์‚ฌ์šฉํ•˜๋ฉด isPresent๋กœ ์‰ฝ๊ฒŒ null์˜ˆ์™ธ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MemberService{
    //repository and constructor
    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }


//Use of Optional & ifPresent
private void validateDuplicateMember(Member member){
    Optional<Member> findMember = memberRepository.findByName(member.getName()); //์—†์„ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ optional๋กœ ๊ฐ์‹ผ๋‹ค
    findMember.ifPresent(m -> {
        throw new IllegalStateException("member already exsists");
    });

    //same code but better!
    memberRepository.findByName(member.getName())
            .ifPresent(m-> { //optional๋กœ ๊ฐ์‹ธ๋ฉด โžก๏ธ ifPresent๋กœ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  throw๊ฐ€๋Šฅ
                throw new IllegalStateException("member already exists");
            });
    }
}

๐Ÿ’ก DI

์„œ๋น„์Šค ๊ฐ์ฒด๊ฐ€ ์ž์‹ ์ด ์˜์กดํ•˜๋Š” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ฐ์ฒด์˜ ๊ตฌํ˜„์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ์™ธ๋ถ€(์„ค์ • ๋“ฑ)์—์„œ ์ „๋‹ฌ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š” ์„ค๊ณ„ ๋ฐฉ์‹

โœ… Member Serice Test

  • use given, when, then

  • โญ๏ธ DI, Before Each
  • memberService Test๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ memberService์™€ ํ•จ๊ป˜ MemoryMemberRepository๋„ ์ƒ์„ฑ๋˜๊ฒŒ ํ•œ๋‹ค
  • use @BeforeEach
  • ๊ทธ๋ฆฌ๊ณ  memberService์— MemoryMemberRepository์ฃผ์ž…
  • โžก๏ธ Dependency Injection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository repository;

    @BeforeEach
    public void beforeEach(){
        repository = new MemoryMemberRepository();
        memberService  = new MemberService(repository); //โญ๏ธ DI
    }

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

    @Test
    void signUp() { // ์ •์ƒ ํ๋ฆ„
        //given
        Member member = new Member();
        member.setName("spring");
        //when
        Long saveId = memberService.signUp(member);
        //then
        Member findMember = memberService.findOne(saveId).get();
        assertThat(findMember.getName()).isEqualTo(member.getName());
    }

    @Test
    void signUpDuplicate() { //ํšŒ์›๊ฐ€์ž…ํ•˜๋Š”๋ฐ ์ด๋ฆ„ ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ
        //given
        Member member1 = new Member();
        member1.setName("spring");

        Member member2 = new Member();
        member2.setName("spring"); //same name trying to signUp
        //when
        memberService.signUp(member1);
        //then
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.signUp(member2));
        assertThat(e.getMessage()).isEqualTo("member already exists");

  • โœ”๏ธ ํšŒ์›๊ฐ€์ž…ํ•˜๋Š”๋ฐ ์ด๋ฆ„ ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ signUpDuplicate()
  • assertThrows(IllegalStateException.class, () -> memberService.signUp(member2))
  • ์˜ˆ์™ธ์ฒ˜๋ฆฌ ํ„ฐ์ง€๋ฉด, IllegalStateException.class
  • ์ด๋ฆ„์ด ๋‹ค๋ฅด๋ฉด ํšŒ์›๊ฐ€์ž… ๊ฐ€๋Šฅํ•˜๋„๋ก
  • ๊ทธ๋ฆฌ๊ณ  ๋ฐ˜ํ™˜ํ•˜๋Š” ์˜ˆ์™ธ์ฒ˜๋ฆฌ ๋ฉ”์„ธ์ง€ assertThat์‚ฌ์šฉํ•ด์„œ ๋น„๊ตํ•˜๊ธฐ

๐Ÿ’ก Spring Bean and DI with annotation

  • @Bean๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•
  • 1๏ธโƒฃ component scan with @Autowired
  • 2๏ธโƒฃ manual bean configuration with @Configuration

1๏ธโƒฃ Spring bean with component scan

  • @Component๊ฐ€ ์žˆ์œผ๋ฉด ์ž๋™์œผ๋กœ Spring bean์œผ๋กœ ๋“ฑ๋ก๋œ๋‹ค.
  • @Controller, @Service, @Repository์€ ๋ชจ๋‘ @Component์˜ ์ผ์ข…์ด๋‹ค
  • @Controller, @Service, @Repository ๋ผ๋Š” annotation์„ ๋ถ™์—ฌ๋‘๋ฉด, ์ƒ์„ฑ ์‹œ spring container๊ฐ€ ์ด๋ฅผ ์•Œ๊ณ  ๊ด€๋ฆฌํ•œ๋‹ค
  • ๐Ÿ‘๐Ÿป annotation์„ ๋ถ™์—ฌ๋†“์œผ๋ฉด ์ž๋™์œผ๋กœ spring bean์— ๋“ฑ๋ก๋œ๋‹ค
  • spring bean์— ๋“ฑ๋ก๋œ๋‹ค๋Š” ์˜๋ฏธ: spring container๊ฐ€ ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค์–ด ๊ด€๋ฆฌํ•œ๋‹ค

DI and Autowired

  • @Controller is dependent on @Service for accessing data
  • @Service is dependent on @Repository for accessing data
  • using @Autowired, can inject spring bean dependencies
  • โžก๏ธ dependency injection
  • @Autowired์„ ์”€์œผ๋กœ์จ controller๋Š” service๋ฅผ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ณ , service๋Š” repository๋ฅผ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ

๐Ÿ‘๐Ÿป Spring Bean์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ์žฅ์ 

  • @Component๋ฅผ ๋ถ™์—ฌ๋†“์œผ๋ฉด ๊ฐ controller, service, repository๋Š” ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค์–ด spring container๊ฐ€ ๊ณต์œ , ๊ด€๋ฆฌํ•œ๋‹ค โžก๏ธ singleton
  • ์˜ˆ๋ฅผ ๋“ค์–ด member controller, order controller์—์„œ ๋ชจ๋‘ member service๋ฅผ ํ˜ธ์ถœํ•˜๋”๋ผ๋„ member service๋Š” ํ•˜๋‚˜๋งŒ ๋งŒ๋“ ๋‹ค
  • spring container์—์„œ ํ•ด๋‹น spring bean์„ ์ฐพ์•„ ์ฃผ์ž…ํ•œ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Controller
public class MemberController {

    private final MemberService memberService;
    @Autowired
    public MemberController(MemberService memberService) { //dependency injection by constructor
        this.memberService = memberService;
    }
}

@Service
public class MemberService {
    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository) { //dependency injection
        this.memberRepository = memberRepository;
    }
}

๐Ÿ’ก Manual spring bean injection

  • 2๏ธโƒฃ Manually configure spring bean

  • annotation์„ ์“ฐ์ง€ ์•Š๊ณ  ๋ณ„๋„ ํŒŒ์ผ๋กœ spring bean์„ ๊ด€๋ฆฌํ•œ๋‹ค
  • @Configuration ํ•˜๊ณ  config ํŒŒ์ผ ์ƒ์„ฑ
  • @Beanํ•˜๊ณ  service, repository๋ฅผ ๋ชจ๋‘ spring bean์— ๋“ฑ๋กํ•œ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class SpringConfig {
    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}
  • ํ•˜์ง€๋งŒ @Controller๋Š” annotation์œผ๋กœ ๋ฌด์กฐ๊ฑด ๋„ฃ์–ด์•ผ ํ•œ๋‹ค

โœ…

โœ…

โœ…

โœ…

โœ…

This post is licensed under CC BY 4.0 by the author.