java 공부를 시작하면서 Spring Boot 강의를 듣기 시작했다. 김영한님 강의는 볼수록 여러가지 배울것이 많아서 도움이 많이 된다.
Spring 은 자바 기반의 웹 어플리케이션을 만들 수 있는 프레임 워크이다.
스프링 프레임워크는 현대 자바 기반의 엔터프라이즈 어플리케이션을 위한 프로그래밍 및 Configuration Model 제공한다'라고 언급하고 있다.
유명한 MVC의 패턴이 사용되며, DI와 AOP 의 특징이 있다. 이것에 관해서는 나중에 따로 정리할 예정이다. 한국에서 사용자가 많은 만큼 방대한 양의 오픈소스와 안정성을 가질 수 있다.
스프링 프레임워크의 특징
현재까지 flask, django, fast-api, node.js , nest.js .. 등등 여라가지 프레임워크를 써보았지만 Spring은 깊게 공부해볼 생각이다.
SpringBoot는 Spring framework 기반 프로젝트를 복잡한 설정없이 간편하고 빠르게 만들어 주는 라이브러리 이다.
원래 Spring 개발 환경 셋팅을 할때 XML 설정 파ㅣ일이라고 web.xml, rootContext.xml, ServletContext.xml 등을 작성하고, 일일이 개발자가 설정을 해주었다면, SpringBoot를 이용하면 보다 쉽고 빠르게 사용할 수 있다. tomcat 같은 was 서버도 자동으로 셋팅이 된다.
Spring boot 장점
스프링부트의 Starter 라이브러리를 등록해서 라이브러리 의존성을 간단히 관리할 수 있다.
SpringStarter 를 통해서 Spring 을 셋팅하고 파일을 생성할 수 있다.
이파일을 통해서 Intellij 를 통해 실행 시키면 된다.
그전에 JDK 를 설치하여 버전에 맞게 세팅을 해야한다 이부분은 생략하도록 하겠다.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
main 메서드이다. java 는 main 메서드가 있어야 실행이 된다.
package spring.basic.spring.basic.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;
}
}
강의 내용중 domain 이라는 패키지를 만들어서, Member라는 class 를 만들어서 getter, setter 하는 내용이다. 기본적인 class 의 구조를 따르고 있다.
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 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());
}
public void clearStore() {
store.clear();
}
}
여기서 볼만한 점은 Optional 이다. 개발을 하다보면 많은 예외 처리를 하는데, Optional을 사용하면 NPE 를 방지할 수 있고. 그러다 보니 로직이 좀더 직관적으로 변할 수 있다.
public interface MemberRepository {
Member save(Member member);
Optional<Member> findById(Long id);
Optional<Member> findByName(String name);
List<Member> findAll();
}
java 의 interface 이다. 기본적인 구조는 TypeScript 에서 쓰던
interface 와 유사하다고 볼 수 있다.
다만 추상클래스와 인터페이스의 차이가 있는데 짚고 가자면
추상클래스는 일반 클래스와 별 다를 것이 없다. 단지, 추상 메서드를 선언하여 상속을 통해서 자손 클래스에서 완성하도록 유도하는 클래스, 그래서 미완성 설계도라고도 표현한다. 상속을 위한 클래스이기 때문에 따로 객체를 생성할 수 없다.
class 앞에 "abstract" 예약어를 사용하여 상속을 통해서 구현해야한다는 것을 알려주고 선언부만 작성하는 추상메서드를 선언할 수 있다
인터페이스는 추상클래스가 미완성 설계도라면 인터페이스는 기본 설계도라고 할 수 있다. 인터페이스도 추상클래스처럼 다른 클래스를 작성하는데 도움을 주는 목적으로 작성하고 클래스와 다르게 다중상속(구현)이 가능합니다.
Repository 를 사용하는 방식도 기존 Nest에서 사용하던 구조와 비슷하다. 강의에서는 db연결없이 memory 를 사용했지만, 실제 db 를 이용하여 구현할때는 많이 달라질것으로 보이지만 사용하는 느낌은 알 것 같다.
가장 중요한 TEST 이다. 우선 이부분은 통째로 가지고 오겠다.
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
/*
같은 Memory Repository 를 사용하기 위해 DI를 사용
DI(Dependency Injection)란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능으로
객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다.
DI(의존성 주입)를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아진다.
*/
}
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
@Test
void 회원가입() {
//given
Member member = new Member();
member.setName("spring");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
}
@Test
void 중복_회원_예외(){
//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("이미 존재하는 회원입니다");
}
@Test
void findOne() {
}
}
우선 TEST에 AfterEach, BeforeEach 와 같이 테스트 시작전, 후로 사용하는 메서드 들이 존재한다. 상황에 맞게 적절히 사용하면 될 것 같다(ex 데이터의 초기화)
spring 에는 junit 이라는 테스트 라이브러리가 있다고 하였는데, 실제 코딩을 하다보면 junit 말고도 한가지다 더 존재해서 상황에 맞게 사용하면 될 것 같다.
Test 의 기본 검증은 given, when, then 의 절차를 따르는게 좋고,
Assertions.assertThat(member).isEqualTo(result); static import 하면 아래처럼 변환가능 alt + enter
static import 를 통해 간단하게 쓸수도 있다.
또한 Intellij 를 쓰면서 많은 단축키 들이 존재하는데 유용한 것들이 만은 것 같다.
getter,setter 를 자동으로 해준다거나, 특정 로직을 Method 로 바꿔준다거나, 해당 class 의 test 기본 구조를 짜준다거나 이런것들이 강의에 많이 포함되어 있어서 좋은거 같다.
현재 절반정도 수강한거 같은데 나머지도 듣고 좀더 정리해볼 예정이다.