[初心-Spring Boot] 예제를 통한 MVC 패턴 설계 (1) - DTO & Controller & Repository

0

초심-spring boot

목록 보기
4/16

1. 비즈니스 요구사항


  • 데이터 : 회원 Id, 이름
  • 기능 : 회원 등록, 조회
  • 아직 DB 저장소가 선정되지 않음.

2. 웹 애플리케이션 계층 구조


  • 컨트롤러: 웹 MVC의 컨트롤러 역할
  • 서비스: 핵심 비즈니스 로직 구현
  • 도메인: 비즈니스 도메인 객체 (DTO)
  • 리포지토리: 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리 (DAO)

3. 클래스 의존 관계


4. DTO 구현


4-1. DTO란?


DTO(Data Transfer Object)는 계층간 데이터 교환을 위한 객체(Java Beans)이다.
DB에서 데이터를 얻어 Service나 Controller 등으터 보낼 때 사용하는 객체를 말한다.

4-2. memberDTO class 제작


memberDTO.java
package com.rptp.rptpSpringBoot.model;

public class MemberDTO {
    private long id;
    private String name;

    public long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

5. Repository 구현


5-1. DAO란?


실제로 DB에 접근하는 객체이다.
Persistence Layer(DB에 data를 CRUD하는 계층)이다.
Service와 DB를 연결하는 고리의 역할을 한다.

5-2. MemberRepository interface 제작



후에 repository 변동 용이성을 위해서 interface를 제작한다.

MemberRepository.java
package com.rptp.rptpSpringBoot.repository;

import com.rptp.rptpSpringBoot.model.MemberDTO;

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

public interface MemberRepository {
    MemberDTO save(MemberDTO memberDTO);
    Optional<MemberDTO> findById(Long id);
    Optional<MemberDTO> findByName(String name);
    List<MemberDTO> findAll();
}

5-3. MemberRepository 실구현 클래스 제작


MemoryMemberRepository.java
package com.rptp.rptpSpringBoot.repository.impliment;

import com.rptp.rptpSpringBoot.model.MemberDTO;
import com.rptp.rptpSpringBoot.repository.MemberRepository;

import java.util.*;

public class MemoryMemberRepository implements MemberRepository {

    private static Map<Long, MemberDTO> store = new HashMap<>();
    private static long sequence = 0L;

    @Override
    public MemberDTO save(MemberDTO memberDTO) {
        memberDTO.setId(++sequence);
        store.put(memberDTO.getId(),memberDTO);
        return memberDTO;
    }

    @Override
    public Optional<MemberDTO> findById(Long id) {
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<MemberDTO> findByName(String name) {
        return store.values().stream()
                .filter(memberDTO -> memberDTO.getName().equals(name))
                .findAny();
    }

    @Override
    public List<MemberDTO> findAll() {
        return new ArrayList<>(store.values());
    }
}

6. Test Code 구현


6-1. 테스트 실행 개요


개발한 기능을 실행해서 테스트 할 때 자바의 main 메서드를 통해서 실행하거나, 웹 애플리케이션의 컨트롤러를 통해서 해당 기능을 실행한다.
이러한 방법은 준비하고 실행하는데 오래 걸리고, 반복 실행하기어렵고 여러 테스트를 한번에 실행하기 어렵다는 단점이 있다.
자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결한다.

6-2. MemoryMemberRepository testCode 작성


MemoryMemberRepositoryTest.java
package com.rptp.rptpSpringBoot.repository.impliment;

import com.rptp.rptpSpringBoot.model.MemberDTO;
import com.rptp.rptpSpringBoot.repository.MemberRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class MemoryMemberRepositoryTest {

    MemberRepository repository = new MemoryMemberRepository();

    @Test
    public void save() {
        MemberDTO memberDTO = new MemberDTO();
        memberDTO.setName("spring");

        repository.save(memberDTO);

        MemberDTO result = repository.findById(memberDTO.getId()).get();
        Assertions.assertEquals(memberDTO, null);
    }
}

@test

junit이 테스트를 시도한다

Assertions.assertEquals( Object , Object );

두 매개변수를 비교하여 같으면 테스트 통과, 다르면 아래와 같이 애러를 호출한다.

Assertions.assertEquals(memberDTO, result);

같으면 위와 같이 tests passed가 뜬다.

MemoryMemberRepositoryTest.java
package com.rptp.rptpSpringBoot.repository.impliment;

import com.rptp.rptpSpringBoot.model.MemberDTO;
import com.rptp.rptpSpringBoot.repository.MemberRepository;
import org.assertj.core.api.Assertions;
//import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

 
class MemoryMemberRepositoryTest {

    MemberRepository repository = new MemoryMemberRepository();

    @Test
    public void save() {
        MemberDTO memberDTO = new MemberDTO();
        memberDTO.setName("spring");

        repository.save(memberDTO);

        MemberDTO result = repository.findById(memberDTO.getId()).get();
        Assertions.assertThat(memberDTO).isEqualTo(result);
    }
}

org.junit.jupiter.api.Assertions 대신 org.assertj.core.api.Assertions를 import받아 위와 같이 사용할 수 있다.

6-3. 남은 테스트 코드 작성


MemoryMemberRepositoryTest.java
class MemoryMemberRepositoryTest {

    MemberRepository repository = new MemoryMemberRepository();

    /*
    @AfterEach
    public void afterEach() {
        //중복 방지를 위하여 각 테스트 종료시 초기화 시켜줘야함

    }
    */
    

    @Test
    public void save() {
        MemberDTO memberDTO = new MemberDTO();
        memberDTO.setName("spring");

        repository.save(memberDTO);

        MemberDTO result = repository.findById(memberDTO.getId()).get();
       // Assertions.assertEquals(memberDTO, result);
        Assertions.assertThat(memberDTO).isEqualTo(result);
    }

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

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

        MemberDTO result = repository.findByName("spring1").get();
        Assertions.assertThat(result).isEqualTo(member1);
    }

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

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

        List<MemberDTO> result = repository.findAll();
        Assertions.assertThat(result.size()).isEqualTo(2);
    }
}


findByName()에서 에러가 날 것이다.
테스트 경우는 save,findByName,findAll 3개 인데, 이들이 선언 순서대로 테스트 하는 것이 아니다. 그래서 findAll이 먼저 실행되고, findByName이 실행되면, finaAll에서 spring1의 이름을 가지는 객체를 생성해서 findByName에서 다른 값으로 인식할 수 있는 것이다.

MemoryMemberRepository.java
public void deleteAll() {
        store.clear();
}

해당 메서드를 추가한다.

MemoryMemberRepositoryTest.java
class MemoryMemberRepositoryTest {

    MemoryMemberRepository repository = new MemoryMemberRepository();


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

repository를 MemoryMemberRepository로 변경하고, afterEach를 추가한다.

@AfterEach

각 테스트가 끝날 때마다 실행하는 메서드에 붙임.

참조


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/lecture/49581?tab=curriculum
DTO, DAO, Entity class의 차이 https://gmlwjd9405.github.io/2018/12/25/difference-dao-dto-entity.html

0개의 댓글