상세 컨텐츠

본문 제목

[스프링 2팀] 스프링 입문 - 섹션 01 ~03

24-25/Spring 2

by dalpaeng4 2024. 9. 27. 10:00

본문

728x90

 

 

 

 

 

1. mvc와 템플릿 엔진


- mvc 란? 

MVC: Model, View, Controller

 

 

 

- Controller 예시

 @Controller
 public class HelloController {
    @GetMapping("hello-mvc")
 public String helloMvc(@RequestParam("name") String name, Model model) {
        model.addAttribute("name", name);
 return "hello-template";
    }
 }

 

 

 

- view 예시

resources/templates/hello-template.html
 `
 ```html
 `
 <html xmlns:th="http://www.thymeleaf.org">
 <body>
 <p th:text="'hello ' + ${name}">hello! empty</p>
 </body>
 </html>

 

 

 

- mvc, 템플릿 엔진 예시

-> 템플릿 엔진 :  템플릿 양식과 특정 데이터 모델에 따른 입력 자료를 합성하여 결과 문서를 출력하는 소프트웨어. 쉽게 말해 서버에서 동적으로 html을 변환하여 웹 브라우저로 보내주는 역할을 한다.

 

  1. 웹 브라우저에서 localhost:8080/hello-mvc 요청
  2. 스프링 부트 내장 톰캣 서버가 스프링 컨테이너의 컨트롤러에서 hello-mvc가 매핑되어있는지 확인
  3. 매핑되어있는 메서드를 호출
  4. hello-template로 리턴, moldel(name(키):Spring(값))
  5. 스프링 부트의 ViewResolver가 리턴값인 hello-template.html을 찾아서 Thymeleaf 템플릿 엔진으로 넘김
  6. Thymeleaf 템플릿 엔진이 변환을 해서 html을 반환

 

 

2. API

 

@RequestBody 어노테이션은 HTTP 요청의 body 내용을 자바 객체로 매핑하는 역할을 하고,
@ResponseBody 어노테이션은 자바 객체를 HTTP 요청의 body 내용으로 매핑하는 역할을 합니다.

 

 

- @ResponseBody 문자 반환

Controller
 public class HelloController {
    @GetMapping("hello-string")
    @ResponseBody
         public String helloString(@RequestParam("name") String name) {
         return "hello " + name;
    }
 }

 

-> @ResponseBody를 사용하면 뷰 리졸버 사용하지 않음

 

-> 스프링 백엔드에서 데이터를 처리하거나 가지고 왔다면, 이 데이터를 View의 영역으로 전달을 해야 한다. 이때 View를 어떤 것을 사용할지 자유롭게 설정을 할 수 있는데 이 설정 역할을 하는 것이 View Resolver

 

 

- @ResponseBody 객체 반환

@Controller
 public class HelloController {
    @GetMapping("hello-api")
    @ResponseBody
     public Hello helloApi(@RequestParam("name") String name) {
     Hello hello = new Hello();
            hello.setName(name);
     return hello;
        }
     static class Hello {
     private String name;
     public String getName() {
     return name;
            }
        }
     }
     ```
     public void setName(String name) {
     this.name = name;
     }
   }
 }

 

- > @ResponseBody를 사용하고, 객체를 반환하면 객체가 JSON으로 변환됨.

 

 

 

 

 

 

3. 회원 관리 예제

 

 

1) 웹 애플리케이션 기본 구조

 

 

-> 컨트롤러: 웹 MVC의 컨트롤러 역할이다.

-> 서비스: 핵심 비즈니스 로직 구현한다.

-> 리포지토리: 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리한다.

-> 도메인: 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리된다.

 

 

 

2) 회원 관리 예제의 비즈니스 요구사항 정리

- 회원 아이디, 회원 이름

- 회원 등록, 조회 기능  

 

 

 

 

 

3) 클래스 의존 관계

 

 

 

 

 

 

4) 회원 객체

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

 

- Member 클래스에서는 id와 name을 설정한다.

 

 

5) 회원 리포지토리 인터페이스

- 회원을 등록하기 위한 리포지토리 작성

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

 

 

 

 

6) 회원 리포지토리 메모리 

- 회원을 조회하기 위한 리포지토리 작성

package hello.hellospring.repository;

 import hello.hellospring.domain.Member;
 import java.util.*;
 /**
 * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
 */
 
 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();
        }
 }

 

 

7) 회원 서비스 개발

 package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import java.util.List;
import java.util.Optional;

 public class MemberService {
     private final MemberRepository memberRepository = new 
    MemoryMemberRepository();
    /**
     * 
회원가입
     */
     public Long join(Member member) {
         validateDuplicateMember(member); //중복 회원 검증
         memberRepository.save(member);
         return member.getId();
        }
     private void validateDuplicateMember(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);
            }
 }

 

 

 

7) 테스트 코드 작성

- 코드를 작성한 후에는 올바르게 실행되는지 테스트코드를 작성하여 확인한다. 

 

7.1) 회원 리포지토리 테스트 코드 작성

 package hello.hellospring.repository;
 import hello.hellospring.domain.Member;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 import java.util.List;
 import java.util.Optional;
 import static org.assertj.core.api.Assertions.*;
 
 class MemoryMemberRepositoryTest {
 	MemoryMemberRepository repository = new MemoryMemberRepository();
    @AfterEach
     public void afterEach() {
            repository.clearStore();
        }
        
        
        @Test
     public void save() {
         //given
         Member member = new Member();
                member.setName("spring");
         //when
                repository.save(member);
         //then
         Member result = repository.findById(member.getId()).get();
         assertThat(result).isEqualTo(member);
      }
      
        @Test
     public void findByName() {
         //given
         Member member1 = new Member();
                member1.setName("spring1");
                repository.save(member1);
         Member member2 = new Member();
                member2.setName("spring2");
                repository.save(member2);
         //when
         Member result = repository.findByName("spring1").get();
         //then
         assertThat(result).isEqualTo(member1);
        }
        
        @Test
     public void findAll() {
         //given
         Member member1 = new Member();
                member1.setName("spring1");
                repository.save(member1);
         Member member2 = new Member();
                member2.setName("spring2");
                repository.save(member2);
         //when
         List<Member> result = repository.findAll();
         //then
         assertThat(result.size()).isEqualTo(2);
        }
 }

 

- @AfterEach : 테스트를 실행할 때마다 DB에 테스트의 결과가 남아있는데, 이럴 경우 다음 테스트가 실패할 가능성이 있다. @AfterEach를 사용하면 DB에 저장된 데이터를 각 테스트가 종료될 때마다 삭제할 수 있다.

 

 

7.2) 회원 서비스 테스트 

package hello.hellospring.service;
 import hello.hellospring.domain.Member;
 import hello.hellospring.repository.MemoryMemberRepository;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import static org.assertj.core.api.Assertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 
 class MemberServiceTest {
     MemberService memberService;
     MemoryMemberRepository memberRepository;
     
    @BeforeEach
    public void beforeEach() {
                memberRepository = new MemoryMemberRepository();
                memberService = new MemberService(memberRepository);
            }
            
    @AfterEach
    public void afterEach() {
                memberRepository.clearStore();
    }
    
    
   	 @Test
     public void 회원가입() throws Exception {
         //Given
         Member member = new Member();
                member.setName("hello");
         //When
         Long saveId = memberService.join(member);
         //Then
         Member findMember = memberRepository.findById(saveId).get();
         assertEquals(member.getName(), findMember.getName());
    }
    
    @Test
 	public void 중복_회원_예외() throws Exception {
         //Given
         Member member1 = new Member();
                member1.setName("spring");
         Member member2 = new Member();
                member2.setName("spring");
         //When
                memberService.join(member1);
         IllegalStateException e = assertThrows(IllegalStateException.class,
                        () -> memberService.join(member2));//예외가 발생해야 한다.
         assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
            }
 }

 

- @BeforeEach : 각 테스트 전에 호출되어 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고, 의존 관계도 새로 맺어준다.

 

 


Q1. 문제 (드래그하여 답 확인해주세요.)

1. mvc 란? 

 답 : MVC: Model, View, Controll

2. 핵심 비즈니스 로직 구현하는 기능을 하는 것은?

답 : 서비스

3. 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리하는 기능을 하는 것은 ?

답 : 리포지토리

4. 웹 MVC의 컨트롤러 역할을 하는 것은 ? 

답 : 컨트롤러

5. 회원 관리 예제를 참고하여 아래에 들어갈 클래스 명을 작성하시오.

답 : 왼쪽에서부터 MemberService -> MemberRepository -> MemberMemoryRepository

 

6. 코드를 작성한 후에는 올바르게 실행되는지 (테스트코드)를 작성하여 확인한다. 

7. API에서 (@ResponseBody)를 사용하면 뷰 리졸버를 사용하지 않아도 된다. 

 

 


Q2. 코드 문제

 

* 아래는 테스트 코드의 한 예시이다. '//여기에 코드 작성' 에 들어갈 코드를 작성해라.

 

1. 답 : @AfterEach

 

 class MemoryMemberRepositoryTest {
 	MemoryMemberRepository repository = new MemoryMemberRepository();
    //여기에 코드 작성
     public void afterEach() {
            repository.clearStore();
        }
        
        ...
 }

 

2. 답 : @BeforeEach

 

 class MemberServiceTest {
     MemberService memberService;
     MemoryMemberRepository memberRepository;
     
   //여기에 코드 작성
    public void beforeEach() {
                memberRepository = new MemoryMemberRepository();
                memberService = new MemberService(memberRepository);
    }
    ...
 }

 

 

출처: 스프링 입문, 코드로 배우는 스프링 부트, 웹MVC, DB접근기술 강의

SPRING #2

Editor : 크리스탈

 

er

ㄴㅇ2ㄹ

 

728x90

관련글 더보기