[Spring-입문] 회원 관리 예제(엔티티, 레파지토리)

DANI·2023년 11월 14일

Spring[김영한T]

목록 보기
3/31
post-thumbnail

📑 비즈니스 요구사항 정리

  • 데이터: 회원ID, 이름
  • 기능: 회원 등록, 조회
  • 아직 데이터 저장소가 선정되지 않음(가상의 시나리오) -> MemoryMemberRepository


1. 회원 도메인 만들기(Entity)

💾 Member

package com.hello.hellospring.domain;

public class Member {

    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;
    }
}

💾 MemberRepository

package com.hello.hellospring.repository;

import com.hello.hellospring.domain.Member;

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

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



💡 Optional이란?


개발을 할 때 가장 많이 발생하는 예외 중 하나가 바로 NPE(NullPointerException)이다. NPE를 피하려면 null 여부를 검사해야 하는데, null 검사를 해야하는 변수가 많은 경우 코드가 복잡해지고 번거롭다. 그래서 null 대신 초기값을 사용하길 권장하기도 한다.

Optional는 null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE가 발생하지 않도록 도와준다. Optional 클래스는 아래와 같은 value에 값을 저장하기 때문에 값이 null이더라도 바로 NPE가 발생하지 않으며, 클래스이기 때문에 각종 메소드를 제공해준다.

public final class Optional<T> {

  // If non-null, the value; if null, indicates no value is present
  private final T value;
   
  ...
}

🔴 Optional 생성하기

  • Optional은 Wrapper 클래스이기 때문에 값이 없을 수도 있는데, 이때는 Optional.empty()로 생성
Optional<String> optional = Optional.empty();

System.out.println(optional); // Optional.empty
System.out.println(optional.isPresent()); // false

  • Optional.of() : 값이 null이 아닌 경우
// Optional의 value는 절대 null이 아님
Optional<String> optional = Optional.of("hello");

  • Optional.ofNullbale() : 값이 Null일수도, 아닐수도 있는 경우
// Optional의 value는 값이 있을 수도 있고 null 일 수도 있다.
Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("anonymous"); // 값이 없다면 "anonymous" 를 리턴


💾 MemoryMemberRepository

package com.hello.hellospring.repository;

import com.hello.hellospring.domain.Member;

import java.util.*;

public class MemoryMemberRepository implements MemberRepository{

    // 키, 값
    // 실무에서는 동시성 문제 때문에 사용하지 않음(ConcurrnetHashMap 사용)
    private static Map<Long, Member> users = new HashMap<>();

    // 실무에서는 동시성 문제 고려해야함
    private static long sequence = 0L;

    @Override
    public Member save(Member member) {
        // 시퀀스 값 올려주기
        member.setId(++sequence);
        // users에 저장
        users.put(member.getId(), member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
         // .ofNullable : 널값을 허용함
        return Optional.ofNullable(users.get(id));
    }

    @Override
    public Optional<Member> findByName(String name) {
        return users.values().stream().filter(x -> x.getName().equals(name)).findAny();
    }

    @Override
    public List<Member> findAll() {
         // users에 저장되어있는 value들의 값을 리스트로 변환 후 반환
        return new ArrayList<>(users.values());
    }
}

💡 Stream 사용법


스트림은 '데이터의 흐름’입니다. 배열 또는 컬렉션 인스턴스에 함수 여러 개를 조합해서 원하는 결과를 필터링하고 가공된 결과를 얻을 수 있습니다. 또한 람다를 이용해서 코드의 양을 줄이고 간결하게 표현할 수 있습니다. 즉, 배열과 컬렉션을 함수형으로 처리할 수 있습니다.

또 하나의 장점은 간단하게 병렬처리(multi-threading)가 가능하다는 점입니다. 하나의 작업을 둘 이상의 작업으로 잘게 나눠서 동시에 진행하는 것을 병렬 처리(parallel processing)라고 합니다. 즉 쓰레드를 이용해 많은 요소들을 빠르게 처리할 수 있습니다.


filter 메서드

  • filter() 메서드는 중개 연산(intermediate operation)을 수행하므로 새로운 스트림을 생성합니다.
  • 특정 조건을 만족하는 요소로 구성된 새로운 스트림을 생성합니다.
  • filter() 메서드의 매개변수는 함수형 인터페이스인 Predicate입니다. 따라서, 람다 표현식도 전달할 수 있습니다.
  • filter() 메서드의 매개변수에는 한 개의 매개변수를 가지며, Boolean 타입의 값을 반환하는 람다 표현식 또는 Predicate 타입의 객체가 전달되어야 합니다.



참고 :







✨ 이번 챕터에서 배운 부분

✅ Stream과 Optional

📝 공부할 부분

✅ Stream 클래스 더 알아보기!!

0개의 댓글