본문 바로가기

WINK-(Web & App)/Spring Boot 스터디

[2024-2 Spring Boot 스터디] 조상혁 #1주차

반응형

이 블로그는 스프링입문 - 코드로 배우는 스프링 부트를 기반으로 쓰여졌습니다.

 

사전 준비

 

spring을 하기 위해서는 java와 IntelliJ의 설치가 미리 되어 있어야 한다고 한다.

11이면 좋다고하니 일단 나의 버전을 확인해보자

 

음 11 은 아니지만 상위 버전이니 차이가 있는지 유의하면서 개발해야겠다.

자바 11 vs 자바 17

혹시나 필요할지 모르니 링크도 찾아두었다.

 

자바(JAVA) 11 설치 및 환경설정 | 자바 11 환경 변수

자바로 코딩 공부를 하려면 개발 할 수 있는 환경을 만들어 주어야 한다. 그 순서를 먼저 작성하자면JAVA SE JDK 설치 -> JAVA 환경 변수 설정 -> IDE 설치 이렇게 볼 수 있겠다. 이 포스팅은 자바로 개

velog.io

 

인텔리제이를 설치해보자

https://goddaehee.tistory.com/195

 

 

설치를 완료하였다.

 

 


 

시작

 

스프링 부트를 시작하기 위하여 https://start.spring.io/ 사이트에 들어가 몇가지 설정을 하는데 영상에서의 설명은 간단히 지나가니 조금만 알아보자

 project : 빌드 관리 도구 / language : 개발 언어 설정 / springboot : 버전 선택 / Dependencies : 라이브러리 

 

설정을 완료후 Ganerate 버튼을 누르면 zip 파일이 생성된다. 

 

생성된 파일의 압축을 풀고 IntelliJ 에서 열면 아래와같은 파일들이 보인다.

 

 

영상에서는 간단하게 2가지를 짚으면서 설명을 하였는데 test와 build.gradle 이다.

 

 

test 는 소프트웨어가 의도한 대로 동작하는지 검증하기 위해 작성하는 코드인데 현대 개발의 트랜드에서 test 코드의 중요성이 높아 지고 있다고 한다.

 

그래서 지금도 그런지 기사를 찾아보았고 오른쪽은

항해99 에서 인터뷰한 내용이다.

 

뉴스보러가기

 

 

 

 

  • plugins: 프로젝트에서 사용할 플러그인을 정의하는 부분입니다. Java 플러그인과 Spring Boot 관련 플러그인들이 설정되어 있습니다.
  • group: 프로젝트의 그룹 ID를 설정합니다. 주로 패키지 이름 형식으로 사용됩니다.
  • version: 현재 프로젝트의 버전을 지정합니다. 여기서는 '0.0.1-SNAPSHOT'으로 개발 중인 버전을 나타냅니다.
  • java: Java 관련 설정을 정의하는 섹션입니다. toolchain을 통해 Java 버전을 명시적으로 지정할 수 있습니다.
  • repositories: 외부 라이브러리를 다운로드할 저장소를 지정합니다. 여기서는 Maven Central 저장소를 사용합니다.
  • dependencies: 프로젝트에 필요한 외부 라이브러리 의존성을 정의합니다.
  • tasks.named('test'): Gradle의 test 태스크를 수정하는 부분입니다. 여기서는 테스트를 JUnit 플랫폼에서 실행하도록 설정합니다.

위는 GPT에게 설명을 부탁하였다.

 

bulid.gradle을 보니까 nodeJS 에서의 package.json이 떠오르는 것 같다. 아마 실제로도 같은 역할을 하는 듯하다.

 


 실습

 

view

 

 

빌드 / 실행

cd Downloads
cd hi-spring   
cd hi-spring   
gradlew build
cd build   
cd libs
java -jar hi-spring-0.0.1-SNAPSHOT.jar

 

정적 파일

서버에서 내 화면으로 보이려면 무조건 get 요청이 있어야 할 것 같았는데 이렇게 접근이 된다니..

궁금해서 옆에 있는 index도 해봤다.

된다..

그래서 밑에 있는 hello.html도 불러봤는데

안된다..

위에 있는 자바파일들은 되나 해봤는데 역시 안된다.

아마 static 폴더에 있는 것들만 가능한 것 같다.

 

흠 더 궁금해서 nodeJS는 어떤가 해보기로 했다.

 

'

이건 안되네

 

추가적으로 알아본 정적 파일을 다루는법

 

Spring에서 정적 리소스를 다루는 네가지 방법

1. Static Resource 사용하기스프링 부트에서는 /resources/static의 파일을 url 요청으로 접근할 수 있다. resources/static/file/hello.html을 접근한다면  /localhost:8080/file/hello.html 기본 리소스 맵핑은 "/**"이지

www.blog.ecsimsw.com

 

MVC 와 탬플릿 엔진

MVC = Model , View , Controller 

개발하면서 생긴 탬플릿 이라고 생각하면 좋다.

view는 보여주는 것에

model은 객체를 관리하는 것에

controller는 동작의 제어에 대해서 모아두고 정리한 것이라고 보면 편할 듯 하다.

 

API

 

 

이것도 좀 더 연습해 보기위해 상속을 적용해서 구현해보자

더보기

고양이 클래스를 만들고 고양이의 품종인 폴드가 상속받게 구성하였다. 

여러 파라미터도 넣어보고 꽤 좋은 경험인 것같다.

중간에 접은귀를 어떻게 넣을지 고민하느라 GPT를 좀 썼다.

 

그냥 넣어본 폴드 고양이..

    @GetMapping("/hello-cat")
    @ResponseBody
    public Cat catApi(@RequestParam("name") String name,
                      @RequestParam("age") int age,
                      @RequestParam("weight") int weight,
                      @RequestParam(value = "foldedEars", defaultValue = "false") boolean foldedEars,
                      @RequestParam(value = "faceShape", required = false) String faceShape) {

        if (foldedEars) {
            Fold fold = new Fold();
            fold.setName(name);
            fold.setAge(age);
            fold.setWeight(weight);
            fold.setFoldedEars(foldedEars);
            fold.setFaceShape(faceShape != null ? faceShape : "Round"); 
            return fold; 
        } else {
            Cat cat = new Cat();
            cat.setName(name);
            cat.setAge(age);
            cat.setWeight(weight);
            return cat; 
        }
    }

    static class Fold extends Cat{
        private boolean foldedEars;
        private String faceShape;

        public boolean isFoldedEars() {
            return foldedEars;
        }

        public void setFoldedEars(boolean foldedEars) {
            this.foldedEars = foldedEars;
        }

        public String getFaceShape() {
            return faceShape;
        }

        public void setFaceShape(String faceShape) {
            this.faceShape = faceShape;
        }
    }

    static class Cat {
        private String name;
        private int age;
        private int weight;

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

        public int getAge() {return age;}
        public void setAge(int age) {this.age = age;}

        public int getWeight() {return weight;}
        public void setWeight(int weight) {this.weight = weight;}

    }
}

 


 

회원관리 예제

 

 

비지니스 요구사항 정리

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

 

 

 

 

요구 사항에 맞춰 개발을 시작해보자.

 

++ getter setter 단축키

더보기

아니 영상을 보는데 복붙은 아니고 갑자기 코드가 마법처럼 생기는 것이 아닌가..!

그래서 내가 놓친것일지 모르겠으나 단축키가 있는 것 같아 찾아보았다.

 

고를 수도 있고 굉장히 좋은 것 같다.. 단축키 진짜 그냥 신이네.. 

테스트 케이스 작성

 

테스트는 서로 의존 관계를 갖거나 공용 데이터에서의 충돌이 일어나지 않도록 매번 데이터들을 다시 지워줘야 한다.

작성 후 -> 테스트 로 진행하였지만 역순으로 진행하여도 괜찮습니다.

 

package helllo.hi_spring.repository;

import helllo.hi_spring.domain.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.List;

public class MemoryMemberRepositoryTest {
    MemoryMemberRepository repo = new MemoryMemberRepository();

    @AfterEach
    public void afterEach(){
        repo.clearStore();
    }
    @Test
    public void testSave() {
        Member member = new Member();
        member.setName("감자");

        repo.save(member);

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

    }

    @Test
    public void testFindByName() {
        Member m1 = new Member();
        m1.setName("오이");
        repo.save(m1);

        Member m2 = new Member();
        m2.setName("당근");
        repo.save(m2);

        Member result = repo.findByName("당근").get();
        Assertions.assertEquals(m2, result);

    }

    @Test
    public void findAll() {
        Member m1 = new Member();
        m1.setName("고구마");
        repo.save(m1);

        Member m2 = new Member();
        m2.setName("배추");
        repo.save(m2);

        List<Member> result = repo.findAll();

        Assertions.assertEquals(2, result.size());
    }


}

 

회원 서비스 개발

 

리포지토리에 있는 클래스들은 저장 검색 등 데이터를 다루는 부분을 관리한다.

서비스는 가져온 데이터를 처리하는 코드가 모인다.  예 ) 회원가입 , 회원조회

 

package helllo.hi_spring.service;

import helllo.hi_spring.domain.Member;
import helllo.hi_spring.repository.MemberRepository;
import helllo.hi_spring.repository.MemoryMemberRepository;

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

public class MemberService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();

    /**
     * 회원가입
     */
    public Long join(Member member) {
        //같은 이름의 회원 x

        validateDuplicateMember(member);
        memberRepository.save(member);
        return member.getId();

    }

    private void validateDuplicateMember(Member member) {
        Optional<Member> result =  memberRepository.findByName(member.getName());
        result.ifPresent(m -> {
            try {
                throw new IllegalAccessException("이미 존재하는 회원입니다.");
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 전체회원 조회
     */
    public List<Member> findMember(){
        return memberRepository.findAll();
    }
    public Optional<Member> findOne(Long memberId){
        return memberRepository.findById(memberId);
    }

}

 

회원 서비스 테스트

 

Given When Than 문법을 지키면 테스트가 좀 더 수월 하다.

 

package helllo.hi_spring.service;

import helllo.hi_spring.domain.Member;
import helllo.hi_spring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memoryMemberRepository;

    @BeforeEach
    public void beforeEach(){
        memoryMemberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memoryMemberRepository);

    }



    @AfterEach
    public void afterEach(){
        memoryMemberRepository.clearStore();
    }

    @Test
    void 회원가입() {
        //given
        Member member = new Member();
        member.setName("안뇽");
        //when
        Long saveId = memberService.join(member);

        //than
        Member findMember = memberService.findOne(saveId).get();
        Assertions.assertEquals(member.getName(), findMember.getName());
    }

    @Test
    public void 중복_회원_예외() {
        // given
        Member m1 = new Member();
        m1.setName("넘버원");

        Member m2 = new Member();
        m2.setName("넘버원"); // 중복 이름으로 설정

        // when
        memberService.join(m1); // m1을 가입시키고

        // IllegalStateException이 발생하는지 확인
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(m2));

        // then
        assertEquals("이미 존재하는 회원입니다.", e.getMessage());
    }


    @Test
    void findMember() {
    }

    @Test
    void findOne() {
    }
}

 

 

1 ~ 4 후기

 

오 이제 좀 가닥이 잡히는 기분이다. 

내가 실력이 된다면 모바일 프로그래밍에서 하는 프로젝트의 서버를 만들어 보는 건 어떨까 하는 생각이 든다...

 

 

반응형