이 블로그는 스프링입문 - 코드로 배우는 스프링 부트를 기반으로 쓰여졌습니다.
사전 준비
spring을 하기 위해서는 java와 IntelliJ의 설치가 미리 되어 있어야 한다고 한다.
11이면 좋다고하니 일단 나의 버전을 확인해보자
음 11 은 아니지만 상위 버전이니 차이가 있는지 유의하면서 개발해야겠다.
혹시나 필요할지 모르니 링크도 찾아두었다.
인텔리제이를 설치해보자
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는 어떤가 해보기로 했다.
이건 안되네
추가적으로 알아본 정적 파일을 다루는법
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 후기
오 이제 좀 가닥이 잡히는 기분이다.
내가 실력이 된다면 모바일 프로그래밍에서 하는 프로젝트의 서버를 만들어 보는 건 어떨까 하는 생각이 든다...
'WINK-(Web & App) > Spring Boot 스터디' 카테고리의 다른 글
[2024-2 SpringBoot 스터디] 탁태현 #1주차 (0) | 2024.10.10 |
---|---|
[2024-2 Spring Boot 스터디] 김아리 #1 주차 (1) | 2024.10.09 |
[2024-2 Spring Boot 스터디] 김문기 #1주 (1) | 2024.10.09 |
[2024 Spring Boot 스터디] 유태근 #3 주차 - 컴포넌트 스캔과 의존관계 자동 주입 (0) | 2024.07.11 |
[2024 Spring Boot 스터디] 남윤찬#2 주차 - 6~7 섹션 (0) | 2024.07.06 |