일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 자바 유클리드
- 프로그래머스 레벨1
- 자바 최대공약수
- replaceAll()
- islowercase()
- 최대공약수와 최소공배수
- isuppercase()
- 자바 스트링
- string과 stringbuilder
- Git사용법
- 래퍼타입
- 스프링
- toLowerCase()
- addDoc
- 동일성과 동등성
- 최소공배수
- 스프링뼈대
- git 컨벤션
- StringBuilder
- while과 two-pointer
- 자바 최소공배수
- 모던자바
- 유클리드호제법
- 베주계수
- stringbuilder의 reverse()
- ineer join
- 최대공약수
- 스프링환경설정
- sql 데이터형 변환
- string
- Today
- Total
주노 님의 블로그
[Spring] 4. 회원관리 예제 (비즈니스 요구사항 정리) 본문
시작하기에 앞서
아래 내용은 김영한님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB접근기술에서 발췌한 내용입니다.
강의자료 일부를 발췌하는건 괜찮다고 하셨다.
무료강의로 지식을 공유해주신 김영한님께 다시한번 감사의 인사를 올리겠습니다
(_ _) (유료도 샀어요!)
회원 관리 예제 비즈니스 요구사항
데이터 : 회원ID, 이름
기능 : 회원 등록, 회원 조회
아직 데이터 저장소가 선정되지 않음(가상의 시나리오)
컨트롤러 : 웹 MVC 컨트롤러 역할을 한다
>> 사용자의 요청을 받고, 그 요청을 처리한 후, 적절한 응답을 제공.
서비스 : 핵심 비즈니스 로직 구현
>> 데이터 처리 로직
리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
>> 데이터베이스에 접근하여, 데이터를 저장하거나 불러오는 역할
도메인 : 비즈니스 도메인 객체, 예)회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장되고 관리됨
>> 데이터베이스에 저장되는것들.
아직 데이터 저장소가 선정되지 않아서 우선 인터페이스로 구현 클래스를 변경할 수 있도록 설계
데이터 저장소는 RDB(관계형데이터베이스), NoSQL 등등 다양한 저장소를 고민중인 상황으로 가정
개발을 진행하기 위해서 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용
도메인 - 데이터베이스에 저장되는것 엔티티라고 생각하면 된다!
package springProject.hello_spring.member_management.domain;
public class Member {
private Long id;
private String name;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(Long id) {
this.id = id;
}
}
요구사항에 정의된 id와 name을 정의하고
그에맞는 getter와 setter를 정의한다
리포지토리 :데이터베이스에 접근, 저장, 변경등을 수행한다
package springProject.hello_spring.member_management.repository;
import springProject.hello_spring.member_management.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
//회원 저장
Member save(Member member);
//특정 id를 가진 도메인을 조회한다
Optional<Member> findById(Long id);
//특정 이름을 가진 도메인을 조회한다.
Optional<Member> findByName(String name);
//저장된 모든 내용을 불러온다
List<Member> findAll();
}
package springProject.hello_spring.member_management.repository;
import springProject.hello_spring.member_management.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository {
//저장받을 맵 생성
private static Map<Long, Member> store = new HashMap<>();
//키값을 올려줄 변수생성
private static long sequence = 0L;
@Override
public Member save(Member member)
{
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
//store에서 하나하나 꺼내라.
// return store.get(id);
//없으면 어떻게>>? optional로 감싸자
return Optional.ofNullable((store.get(id)));
}
@Override
public Optional<Member> findByName(String name) {
//store에서, member에서 위 파라미터에서 넘어온 name과 같다면 반환한다.
//findAny는 하나만 찾는것 있다? 뒤를 조사 안하고 나오기..
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
//store에 있는 member들을 모두 꺼낸다.
return new ArrayList<>(store.values());
}
}
여기서 한번 코드가 잘 돌아가는지 테스트 해볼 수 없나??
테스트 케이스를 추가하면 된다!
테스트 케이스 사용법
https://juno0432.tistory.com/29
회원 서비스 개발
서비스는 비즈니스의 핵심 로직을 구현하는 단계이다
기능인 회원 등록과 회원 조회를 구현해본다.
먼저 service 패키지에 MemberService를 만들고.
public Long join(Member member)
{}
회원가입을 하는 join을 만들어준다
//조건 추가 : 같은 이름이 있는 중복 회원은 안된다!
Optional<Member> result = memberRepository.findByName(member.getName());
//기존 멤버가 있으면, if null이 아니면<< 아래 에러를 던져준다
// null일 가능성이 있으면 요즘은 optional로 감싼다. result.orElseGet() << 있으면? 없으면?
result.ifPresent(m -> { throw new IllegalStateException("이미 존재하는 회원이다"); });
일단 null이 들어갈 수 있으면 optional로 감싼다
회원가입을 하는데 동일한 아이디가 많으면 안되니까 조정을해준다
ifPresent<< 를 사용한다
만약에 있다면 익셉션을 던져준다
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m-> {
throw new IllegalAccessException("이미 존재하는 회원입니다..");
});
}
가독성있게 메서드를 분리해준다.
데이터에서 findByName을 하여 지금 등록된 id와 데이터베이스에 아이디가 존재하면 오류를 반환해준다
public List<Member> findMembers()
{
memberRepository.findAll();
}
전체 회원 조회는 findAll()을 사용한다
public Optional<Member> findOne(Long memberId)
{
return memberRepository.findById(memberId);
}
한 회원 조회는 위와 같이 설계한다.
Service 테스트하기
repository는 직접 폴더를 생성해서, 클래스를 생성했지만 간단한 기능이 있다
윈도우 기준 ctrl + shift + t를 입력하면
아래와 같은 창이 뜬다
모든 메서드를 테스트 해야하니 다 체크하고 ok를 누르면
package springProject.hello_spring.member_management.service;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MemberServiceTest {
@Test
void join() {
}
@Test
void findMembers() {
}
@Test
void findOne() {
}
}
위와같이 자동으로 생성해준다.
테스트 에서는 메서드 이름을 한글로 설정해도 된다.
빌드할때 들어가지 않을 뿐더러 테스트용이니까 이해하기 쉽게쉽게..
그리고 service test코드를 작성할때 Given - when - then 패턴을 사용하여 구현하면 좋은데
Given : 테스트의 사전 조건 설정
Wehn : 테스트할 실제 동작 수행
Then : 테스트 결과를 검증하는 부분.
으로 구성된다
@Test
void 회원가입()
{
//given - when - then패턴을 사용하면 가독성과 유지보수가 좋다!
//given 테스트의 사전조건
Member member = new Member();
member.setName("hello");
//when 실행했을때, 동작을 수행하는 부분
Long saveId = memberService.join(member);
//then 결과가 ~ 나와야한다.
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
}
위 예제를 보면
회원가입 로직을 테스트하는데
given에서는 member를 세팅해준다
when에서는 회원가입을 수행해준다
then에는 원하는 결과와 실제 결과를 비교해준다
그런데 테스트 에서는
잘 실행되는지 뿐만아니라
예외사항에서 나는 대처도 해야한다.
@Test
public void 중복_회원_예외(){
//given
Member member1 = new Member();
member1.setName("Spring");
Member member2 = new Member();
member2.setName("Spring");
//when >> validate에서 걸려야함
memberService.join(member1);
// try {
// memberService.join(member2); //실행이된다면
// fail(); // 이쪽으로 넘어갈 일이 없음!
// }catch (IllegalArgumentException e){
// assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원이다");
// }
//try catch 보다 더 좋은 방법
IllegalStateException e =
assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원이다");
//then
//예외 처리가 되어야함..
}
만약 중복된 이름이 등록이 되었을 시, 에러를 표시하거나 처리해야한다
위 try-catch문과 아래 IllegalState~은 같은 예이다
실행을 하면 정상적으로 처리되는 것을 볼 수 있다
에러가 발생했을텐데 왜 정상처리냐!
assertThat은 기댓값과, 실제값의 일치 여부를 계산하기때문에
익셉션 메세지와 내가 지정한 메세지가 같으면 정상이라는 뜻이다.
물론 전체를 테스트하면 똑같은 오류가 뜰 것이다..
중복 회원 예외에서 spring을 등록하였고, 회원가입에서 또 등록을 하고 있다.
//어떤 행동이 끝나면 실행되는 메서드
@AfterEach
public void afterEach()
{
memberRepository.clearStore();
}
똑같이 한 메서드의 실행이 끝나면 데이터를 초기화해준다.
'공부 > Spring' 카테고리의 다른 글
[Spirng] 5. 스프링 빈과 의존관계 설정 (0) | 2024.07.28 |
---|---|
[Spring] 4. 회원관리 예제 (테스트케이스 작성법) (0) | 2024.07.28 |
[Spring] 3. 서버가 클라이언트에게 페이지를 전달하는 방식 (API) (0) | 2024.07.27 |
[Spring] 3. 서버가 클라이언트에게 페이지를 전달하는 방식 (MVC와 템플릿 엔진) (0) | 2024.07.27 |
[Spring] 3. 서버가 클라이언트에게 페이지를 전달하는 방식 (정적 콘텐츠) (0) | 2024.07.27 |