Notice
Recent Posts
Link
Tags
- Docker
- 문자열
- 데이터베이스
- hibernate
- string
- DI
- 스프링
- sql
- spring security 6
- static
- mysql
- springboot
- join
- spring boot
- spring
- @transactional
- 프로그래머스
- Django
- nginx
- 자바
- 1차원 배열
- spring mvc
- java
- select
- PYTHON
- AWS
- 스프링부트
- jpa
- ORM
- SSL
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Archives
개발하는 자몽
스프링 부트 공부 (5), 회원 관리 예제 - 백엔드 본문
위 강의를 바탕으로 작성 중입니다.
비즈니스 요구사항 정리(쉬운 버전)
- 데이터 : 회원 ID, 이름
- 기능 : 회원 등록, 조회
- 아직 데이터 저장소가 선정되지 않음(가상의 시나리오)
일반적인 웹 애플리케이션 계층 구조
- 컨트롤러(Controller) : 웹 MVC의 Controller 역할
- 서비스(Service) : 핵심 비즈니스 로직 구현
- 리포지토리(Repository) : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
- 도메인(Domain) : 비즈니스 도메인 객체. ex) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨
회원 도메인, 리포지토리 만들기
회원 도메인
- ID : 이메일 값 → 고객이 정하는 것이 아닌 구분을 위해 시스템이 저장하는 ID(식별자)
- name : 이름
- private 객체 사용을 위한 getter, setter 생성
- IntelliJ getter, setter 단축키 : Alt + Insert (윈도우)
리포지토리
- 회원 객체를 저장하는 저장소
- 인터페이스로 한 이유 : 아직 데이터 저장소가 선정되지 않아서, 우선 인터페이스로 구현 클래스를 변경할 수 있도록 설계
package Hello.hellospring.repository;
import Hello.hellospring.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository { //Alt+Enter로 메소드 전부 생성
/* 저장(save)를 할 때 어딘가에 저장해야함 : Memory */
//실무에서는 동시성 문제가 있을수도 있어서 공유되는 변수일 때는 ConcurrentHashMap사용
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L; // 0, 1, 2, ... 키 값 생성
@Override
public Member save(Member member) { //사용자 저장
member.setId(++sequence); //sequence 값 1 증가시키기
store.put(member.getId(), member); //ID 세팅 후 저장장
return member;
}
@Override
public Optional<Member> findById(Long id) {
//결과가 없으면 null을 반환할텐데
//Optional.ofNullable로 감싸서 반환하면 클라이언트에서 처리(?) 가능
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name)) //인자로 넘어온 name이 같은 경우에만 filtering
.findAny(); //찾으면 반환
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
}
테스트는 어떻게 할까?
테스트 케이스 작성
- 내가 원하는 대로, 정상적으로 동작하는지 검증
- 코드를 코드로 검증
- 각 메서드 테스트 시 메서드 실행 순서가 결과에 영향을 미쳐서는 안 됨(의존관계 X)
- 어떤 메서드가 실행되면서 리포지토리에 데이터 저장되고 다른 메서드 실행 시 결과에 영향을 줄 수도 있음
- @AfterEach와 clear() 함수를 활용해서 리포지토리를 각 메서드 테스트 실행 후에 비우자
- JUnit 프레임워크로 테스트 실행
save 메서드 테스트
package Hello.hellospring.repository;
import Hello.hellospring.domain.Member;
import org.junit.jupiter.api.Test;
//다른데에서 쓸 것이 아니기 때문에 굳이 public으로 안해도 됨
class MemoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
/**
* @Test 어노테이션 :
* org.junit.jupiter api
* 해당 annotaion을 추가함으로써 메소드를 그냥 실행할 수 있음
*/
@Test
public void save() { //save 테스트
Member member = new Member();
member.setName("jaamong");
//리포지토리에 save
repository.save(member); //save 메소드에서 Id 세팅
//제대로 저장이 됐는지 확인
Member result = repository.findById(member.getId()).get(); //반환 타입 : Optional -> Optional에서 값을 꺼낼 때는 get()으로 가능
//검증 : 저장한 거랑 db에서 꺼낸 거랑 동일하면 true
//단순하게
System.out.println("result = " + (result == member));
}
}
결과 1(단순한 방식의 검증)
매번 저렇게 출력할 수는 없으니 Assertions를 사용하자
기대하는 결과 : 저장했던 member가 findById 하면 나와야 함
결과 2(Assert 사용 방식의 검증)
- 출력 X
- 왼쪽을 보면 초록색으로 체크 표시가 보임 → 성공
- null을 집어넣으면 빨간불
- Expected를 보면 기대한 값이 나오는데 해당 값을 주지 않고 Actual : null을 줘서 해당 난리가 남
findAll 메서드 테스트
- save 2명 : jaamong, aejaamong
- result 사이즈와 actual 2 비교 → 성공
회원 서비스 개발, 테스트
개발
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 {
//TODO : 회원 서비스를 만드려면 회원 리포지토리가 있어야 함
private final MemberRepository memberRepository = new MemoryMemberRepository();
/**
* 회원가입
*/
public Long join(Member member){
//같은 이름이 있는 중복 회원은 안됨
//findByName 리턴 형태가 Optional Member이니까 => Optional Member.ifPresent ~~~
validatedDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}
private void validatedDuplicateMember(Member member) { //메소드 추출 : ctrl + alt + m
memberRepository.findByName(member.getName())
.ifPresent(m -> { //ifPresent : null이 아니라 만약 값이 있으면, m : member => member에 값이 있으면
throw new IllegalStateException("이미 존재하는 회원입니다."); // null이 아니라 만약 값이 있으면 throw
});
}
/**
* 전체 회원 조회
*/
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
테스트
쉽게 테스트 코드 만들기
- 테스트 코드를 만들기 원하는 클래스 클릭 후
- 단축키 : ctrl + shift + t
- Create New Test 뜸 (→ Testing library : JUnit5 선택했음)
지금은 중복되는 이름이 없어서 성공
테스트할 때 DB랑 객체 선언 관련해서 더 신경 써야 되는 게 많은데 일단 보류..
'Java & Kotlin > Spring' 카테고리의 다른 글
[Spring Boot] 패키지 구조 (0) | 2022.01.07 |
---|---|
[Spring, 이클립스] Gradle 프로젝트 생성, Spring MVC 환경 구축 (2) | 2022.01.06 |
스프링 부트 공부 (4), 스프링 웹 개발 기초 (0) | 2022.01.03 |
스프링 부트 공부 (3), View 환경설정, 빌드 및 실행 (0) | 2022.01.03 |
스프링 부트 공부 (2), 라이브러리 (0) | 2022.01.03 |
Comments