🙆

【初心者向け 092】Spring入門2(API作成、テスト)

2023/09/23に公開

はじめに

Infleanから勉強した内容を紹介するため、画像の場合はスクラップしました。リンクはこちらです。
https://www.inflearn.com/course/스프링-입문-스프링부트/dashboard

API

Springはhtmlを送る方式(static,template html)と直接値を
送る方式があります。今日は値を送る方法について、勉強します。
前回との違いとして、@ResponseBodyというアノテーションが追加になり、ModelとaddAttributeメソッドを活用せずに、そのまま変数をリターンします。

BodyはhtmlのBodyではなく、httpのbody messageです。

@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name){
	return "hello" + name; //hello spring
   }
}
hello JOJO

Objectで送る方法(json)

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

DBアクセス

modeling

全てはオブジェクト(インスタンス化)で処理します!OOP!

コントローラー(컨트롤러):WEB MVCのコントローラーの役割。

サービス(서비스) : ビジネスロジック具象(IDは重複禁止、一人の会員は重複ではいけない)。サービスを利用することにおいてのルールみたいなやつ

ドメイン(도메인) : 会員、注文、クーポンなどDBに保存されるオブジェクト。

レポジトリ(리포지토리) :ドメインオブジェクトを通して、ビジネスロジックが実現できるようにするオブジェクト。また、分からないです!

まだ、DBをRDBにするかNoSQLにするか決まってない状況なので、インターフェースから具象クラスを変更できるように設計します。

MemberというDTO(Java Bean)、InterfaceであるMemberRepositoryを作成します。(long id, String name)
その後、Interface具象したMemoryMemberRepositoryを作成します。

repository

dataの処理を担当するオブジェクト。DBと直接連携するオブジェクトです。

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import java.util.*;

public class MemoryMemberRepository implements MemberRepository{

    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L; //intかlongかを区切る

    @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 Optional<Member> findByName(String name) {
        return store.values().stream()
                .filter(member -> member.getName().equals(name))
                .findAny();
    }

    @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values());
    }
}

元々はConcurrentHashMapを利用した方が良いです。
ConcurrentHashMap: 実務で使用するThread-safeのHashMap
AtomicLong: 実務で使用するThread-safeのWrapper class(long)

Test Case

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class MemoryMemberRepositoryTest {

    MemberRepository repository = new MemoryMemberRepository();

    @Test
    public void save() {
        Member member = new Member();
        member.setName("spring");

        repository.save(member);

        Member result = repository.findById(member.getId()).get();
        Assertions.assertEquals(member,result);
    }

}

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class MemoryMemberRepositoryTest {

	MemberRepositoryTest repository = new MemoryMemberRepository();
	
	@Test
	public void save(){
	   Member member = new Member();
	   member.setName("spring");
	   
	   repository.save(member);
	   
	   Member result = repository.findById(Member.getID()).get();
	   Assertions.assertEquals(member,"adsdzczxc");
	} 
    }

}

@AfterEach

テストコードは、一気にテストする際には順序がランダムで行われるので、一つのテストコードが終われば、データーをクリアーする必要があります。

そのためには、まず本物のクラスに以下のようなコードを作成します。

Original
  public void clearXXX(){
        xxx.clear();
    }

その後、テストクラスには@AfterEachを付け、同じくclear()するメソッドを作成します。

OriginalTest
  @AfterEach
  public void afterEach(){
    repository.clear();
  }

Service

サービス(サイト)のロジックをRepository(DAOと似てる)を連携しながら、DBのデーターをチェックし、クライアントからもらったデーターをチッェクします。
会員等億でIDが重複ではいけないのようなルールです。

public class MemberService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    //Repositoryと連携するので、オブジェクトを部品として、使います。(Has関係)
   	 
    public Long join(Member member) {
	validateDuplicateMember(member); 
        //同じ名前があるかをチェックするメソッド
	//(下にExctractしたので、下からご覧ください!)
        
	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);
    }
}

Repositoryを部品として、呼び出しメソッドを使って、クライアントをチェックするので、積極的に活用していることが分かります。初めて見るパターンです!

Discussion