Domain & Repository & Service & View & Controller

Codren·2021년 9월 7일
1

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

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

  • 컨트롤러 - 웹 MVC의 컨트롤러 역할
  • 서비스 - 컨트롤러에 필요한 핵심 비즈니스 로직 구현
  • 리포지토리 - 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리 (Dao)
  • 도메인 - 비즈니스 도메인 객체 (Entity,Model),
    예) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨




2. Interface 를 통한 느슨한 결합

  • 특정 데이터 저장소가 선정되지 않을 때, 인터페이스로 구현 클래스를 변경할 수 있도록 설계
  • 초기 개발 단계에서 구현체로 DB 가 아닌 메모리 기반의 데이터 저장소 사용



Section 2. Domain

1. 회원 도메인

  • 회원 객체 (Entity, Model)
package 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;
 }
}



Section 3. Repository

1. 회원 리포지토리 인터페이스

  • 리포지토리 구현체가 공통적으로 수행하게 될 메서드 등을 지정
package hello.hellospring.repository;
import 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();
}




2. 회원 리포지토리 메모리 구현체

  • 실무에서는 동시성 문제로 인해서 ConcurrentHashMap, AtomicLong 사용 고려
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.stereotype.Repository;
import java.util.*;

@Repository
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) {
 	return Optional.ofNullable(store.get(id));
 }
 
 @Override
 public List<Member> findAll() {
 	return new ArrayList<>(store.values());
 }
 
 @Override
 public Optional<Member> findByName(String name) {
 	return store.values().stream()
 		.filter(member -> member.getName().equals(name))
 		.findAny();
 }
 public void clearStore() {
 	store.clear();
 }
}



Section 4. Service

1. 회원 서비스

  • Service 는 Business 적인 메서드와 메서드명
  • Repository 는 단순 DB 작업 메서드와 메서드명
package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

@Service
public class MemberService {
    private final MemberRepository memberRepository;

    // Dipendency Injection
    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    
    //    @Autowired
    //    public void SetMemberRepository(MemberRepository memberRepository) {
    //        this.memberRepository = memberRepository;
    //    }

    // 회원가입
    public Long join(Member member){

        validationDuplicateMember(member);  // name 중복 검증
        memberRepository.save(member);
        return member.getId();
    }

    // name 중복 검증 메서드
    private void validationDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
                .ifPresent(m ->{
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });
    }

    // 전체 회원 조회
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }

    // 한명의 회원 조회
    public Optional<Member> findOne(Long memberId) {
        return memberRepository.findById(memberId);
    }

}




2. 회원가입 메서드 리팩토링

    ① ifPresent() 메서드를 통해 getName() 으로 반환되는 객체가 있는지 확인


     ② 반환되는 객체는 어차피 Optional 타입이므로 메소드 체이닝으로 리팩토링


     ③ 해당 부분을 메서드로 Extract (crtl + t)


     ④ 리팩토링 결과




Section 5. View

1. Home

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
    <div>
        <h1>Hello Spring</h1>
        <p>회원 기능</p>
        <p>
            <a href="/members/new">회원 가입</a>
            <a href="/members">회원 목록</a>
        </p>
    </div>
</div>
</body>
</html>




2. 회원 등록

  • POST Method 방식으로 'name' 데이터를 보냄
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
    <form action="/members/new" method="post">
        <div class="form-group">
            <label for="name">이름</label>
            <input type="text" id="name" name="name" placeholder="이름을 입력하세요">
        </div>
        <button type="submit">등록</button>
    </form>
</div>
</body>
</html>




3. 회원 조회

  • Thymeleaf 템플릿 엔진 이용
  • member 의 id, name 을 직접 접근하는 것이 아닌 Setter 메서드 이용
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
  <div>
    <table>
      <thead>
      <tr>
        <th>#</th>
        <th>이름</th>
      </tr>
      </thead>
      <tbody>
      <tr th:each="member : ${members}">
        <td th:text="${member.id}"></td>
        <td th:text="${member.name}"></td>
      </tr>
      </tbody>
    </table>
  </div>
</div>
</body>
</html>



Section 6. Controller

1. 회원 등록 POST 값을 저장할 Controller

  • POST 입력의 'name' 값을 저장할 객체
package hello.hellospring.controller;

public class MemberForm {

    private String name;

    public String getName() {
        return name;
    }

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




2. 회원 관리 컨트롤러

  • POST 요청의 'name' 값이 MemberForm 멤버변수에 담겨 생성되고 create 메서드의 매개변수로 전달
  • Member 모델 생성 -> 회원 등록 Service 기능 호출
  • 회원 조회 Service 기능 호출하여 Model 로 넘김
package hello.hellospring.controller;
...

@Controller
public class MemberController {

    private final MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

    @GetMapping("/members/new")
    public String createForm() {
        return "members/createMemberForm";
    }

    @PostMapping("/members/new")
    public String create(MemberForm form) {
        Member member = new Member();
        member.setName(form.getName());
        memberService.join(member);
        return "redirect:/";
    }

    @GetMapping("members")
    public String list(Model model) {
        List<Member> members = memberService.findMembers();
        model.addAttribute("members", members);
        return "members/memberList";

    }
}




3. 최종적인 Component 관계

  • Controller 는 Service 기능을 이용하여 Business Logic 처리
  • Service 는 Repository 기능을 이용하여 DB 작업 수행
  • Repsository 는 실제 SQL 문을 이용하여 DB 접근

Component Annotation 참고

0개의 댓글