
Java 11 또는 그 이상 버전IDE (ItelliJ IDEA)https://start.spring.io/위의 사이트는 spring에서 운영하고 있는 사이트이고, 프로젝트를 손쉽게 생성해준다.세팅화면을 살펴보자.Project 종류크게 Maven 과 Gradl

프로젝트 창에서 External Libraries 라는 폴더가 있는데, 이 곳에서 우리가 가져온 라이브러리들을 볼 수 있다. 많은 양의 라이브러리들이 있다면 한 눈에 살펴보기가 어려운데 이럴 때 Gradle 탭을 활용하면 된다.위의 사진과 같이 Gradle 탭을 열어

저번에 빌드를 해서 localhost:8080에 접속을 해보았다. 아직 아무 파일과 코드들이 없었기 때문에 에러페이지만을 보여줬는데, 이번에는 welcome page를 만들어보자. spring boot는 resources/static/index.html 파일을 넣으면
웹을 개발하는 방법으로는 크게 3가지 방법이 있다. 정적 컨텐츠 (static contents) MVC와 템플릿 엔진(template engine) API 오늘은 위의 3가지 방법중 정적컨텐츠를 알아보자 💡 정적 컨텐츠 정적 컨텐츠란? > 정적 컨텐츠란 서버의 도

MVC란 Model, View, Controller의 약자이다.View : 화면에 보여지는 관련된 일Controller : 비즈니스 로직 서버에 관련된 일Model : 화면에 필요한 자료들을 담에서 화면에 표시하는 일같은 일들의 역할을 나눈것이 MVC 패턴이라고 볼 수

Spring 개발에서 이야기하는 API 방식이란, JSON 데이터 구조 포멧을 이용하여 클라이언트에게 데이터를 전달하는 방식이다.@ResponseBody란?HTTP의 body에 문자 내용을 직접 반환viewResolver 대신에 HttpMessageConverter 가

5단계에 걸쳐 회원 관리하는 예제를 만들어보자.단계는 아래와 같다.비즈니스 요구 사항 정리회원 도메인과 레포지토리 만들기회원 레포지토리 테스트 케이스 작성회원 서비스 개발회원 서비스 테스트이번 예제의 목표는 스프링 생태계 전반적으로 개발을 어떤식으로 진행하고, 동작하는

domain이라는 package를 만들고 Member 클래스를 생성하자.Member 클래스에서는 회원의 정보가 담긴 클래스이다.비즈니스 요구사항에서 정한 회원에 데이터는 Id와 이름이 있다.Id는 회원이 직접 작성하는 값이 아닌, 데이터 구분을 위해서 시스템이 정한 I

💡 테스트 코드란? 앞서 만들었던 코드들의 기능이 제대로 작동하는지 확인을 하는 방법은 다양하다. main 메서드를 통해서 실행 컨트롤러를 통해서 해당 기능을 실행 이러한 과정은 오래걸리고 반복적으로 실행하기에 어렵다는 단점이 있다. 따라서 우리는 jUnit 이라

이번에는 회원 서비스 클래스를 만들어보자.회원 서비스는 회원 도메인과 레포지토리를 활용하여 실제 회원들이 사용할 비지니스 로직을 작성하는 것이다.먼저 java에 service라는 package를 만들어준다. 그리고 service 패키지에 MemberService 클래스

이전에 Repository 기능을 테스트 할 때는 package와 class를 직접 만들었다.테스트 클래스를 자동으로 생성하는 법을 알아보자.테스트 해보고싶은 class에서 ctrl + shift + T 를 누른 후 Create new Test를 클릭해주면 아래와 같은

앞서 만들었던 service와 repository를 구현하는 화면을 붙이려면 컨트롤러를 만들어야한다. 컨트롤러는 memberservice를 통하여야하는데 그걸 의존관계라고 한다. controller 패키지에 MemberController를 만들고 아래와 같이 어노테이션

이번에는 자바 코드로 직접 스프링 빈에 등록해보자.MemberService 클래스에서 전에 만들었던 @Service 어노테이션. @Autowired 어노테이션을 지운다. repository에서도 @Repository 어노테이션을 지운다.실행해보면 역시 MemberSer

웹 mvc로 회원관리 예제를 개발해보자먼저 홈 화면을 추가해보자.controller 패키지에서 homeController를 만들고 아래와 같은 코드를 작성하자.home을 리턴하였기에 template 패키지에 home.html을 생성한다.localhost:8080에 접속

이전 시간에는 메모리에만 저장되었어서 서버가 재시작되면 삭제된다.따라서 실무에서는 데이터베이스에 저장을 한다.실무에서는 MYSQL등의 데이터베이스를 많이 사용하지만 우리는 교육용으로 좋은 H2 데이터베이스를 설치해보자. 아래 링크를 통해 다운로드 하면 된다.https&

JDBC 방법은 상당히 오래된 방법으로 잘사용하지 않지만 여러가지 방법들을 비교하며 학습하기 위해 JDBC방법으로 DB를 연결해보자.build.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리를 추가해야 한다.application.properties에 데
지금까지느 순수한 자바코드로 테스트하였다.이제부터는 테스트를 스프링이랑 엮어서 해보자.MemberServiceTest 대신 MemberServiceIntegrationTest라는 클래스를 만들어준다. @SpringBootTest, @Transactional 어노테이션을
스프링 JdbcTemplate에 대헤서 알아보자.스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해준다. 하지만 SQL은 직접 작성해야 한다.사용방법은 아래와같다. 생성자가 하나라면 @Autowired 생략
JDBC에서 JDBC Template 변경해보면 코드가 확 간결해졌지만,SQL은 사용자가 작성해야한다. JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패

스프링 부트와 JPA만 사용해도 개발 생선성이 증가하고, 코드가 간결해진다. 여기에 스프링 데이터 JPA를 사용하면, 더욱더 큰 효과를 낼 수 있다. 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 할 수 있고, 반복 개발해온 CRUD 기능도 스프링 데이터 JPA
AOP가 필요한 상황을 알아보자모든 메소드의 호출 시간을 측정하고 싶다면?공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern)회원 가입 시간, 회원 조회 시간을 측정하고 싶다면?위의 상황 중 메서드의 호출 시간을 측정하

AOP란 Aspect Oriented Programming의 약자로,공통 관심 사항(cross-cutting concern)과 핵심 관심 사항(core concern)을 분리하는것이다.전 시간에 했던 시간 측정 로직을 AOP를 적용시켜 수정해보자.AOP패키지를 만들고

💡 스프링이란? 스프링 생태계 스프링이란 특정한 하나의 기술이아니라 여러가지 기술의 모음이라고 할 수 있다. 아래의 기술외에도 다양한 기술들이 있다. 자세한 정보는 https://spring.io/ 에서 확인 가능하다. 가장 핵심은 스프링 프레임워크이고 이 모든 기

아래와 같은 설정값으로 프로젝트를 생성한다.빌드만 Gradle에서 IntelliJ IDEA로 바꿔준다.회원회원은 가입하고 조회할 수 있다.회원은 일반과 VIP 두 가지 등급이 있다.회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)주
새로운 할인 정책을 확장해보자. 지금까지는 고정 금액 할인이였는데 좀 더 합리적인 정률%할인으로 변경하고 싶다. 우리는 유연하게 설계가 가능하도록 객체지향 설계 원칙을 준수하였다. 잘 준수하였는지 확인해보자. RateDiscountPolicy만 추가 개발하면 될 거

새로운 할인 정책을 적용해보자.할인 정책을 변경하려면 클라이언트인 OrderServiceImpl 코드를 고쳐야 한다.역할과 구현을 충실하게 분리하였고 다형성을 활용하고 인터페이스와 구현 객체를 분리하였다. 하지만 문제점이 있다.OCP와 DIP같은 객체지향 설계 원칙을

애플리케이션은 실제 실행되는 객체는 본인의 역할을 수행, 어떤 구현체가 들어갈지는 기획자가 해야한다.애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스를 만들자.AppConfig는 애플리케이션

현재 AppConfig를 보면 중복이 있고, 역할에 따른 구현이 잘안보인다.리팩터링 전이제 중복을 제거하고, 역할에 따른 구현이 보이도록 리팩터링 하자.리팩터링 후new MemoryMemberRepository() 이 부분이 중복 제거되었다. 이제 MemoryMembe

처음으로 돌아가서 정액 할인 정책을 정률 % 할인 정책으로 변경해보자.FixDiscountPolicy -> RateDiscountPolicy할인 정책 변경 구성 코드AppConfig 에서 할인 정책 역할을 담당하는 구현을 FixDiscountPolicy RateDisc
3가지 SRP, DIP, OCP 적용한 클래스는 하나의 책임만 가져야 한다.클라이언트 객체는 직접 구현 객체를 생성하고, 연결하고, 실행하는 다양한 책임을 가지고 있음SRP 단일 책임 원칙을 따르면서 관심사를 분리함구현 객체를 생성하고 연결하는 책임은 AppConfig

기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행했다. 한 마디로 구현 객체가 프로그램의 제어 흐름을 스스로 조종했다. 개발자 입장에서는 자연스러운 흐름이다.반면에 AppConfig가 등장한 이후에 구현 객체는 자신의 로
지금까지 순수한 자바코드로만 DI를 적용했다. 이제 스프링을 사용해보자.AppConfig에 설정을 구성한다는 뜻의 @Configuration을 붙여준다.각 메서드에 @Bean을 붙여준다. 이렇게 하면 스프링 컨테이너에 스프링 빈으로 등록한다.두 코드를 실행하면 스프링

ApplicationContext 를 스프링 컨테이너라 한다.ApplicationContext 는 인터페이스이다.스프링 컨테이너는 XML을 기반으로 만들 수 있고, 애노테이션 기반의 자바 설정 클래스로 만들 수 있다.직전에 AppConfig 를 사용했던 방식이 애노테이
스프링 컨테이너에 실제 스프링 빈들이 잘 등록 되었는지 확인해보자.모든 빈 출력하기실행하면 스프링에 등록된 모든 빈 정보를 출력할 수 있다.ac.getBeanDefinitionNames(): 스프링에 등록된 모든 빈 이름을 조회한다.ac.getBean(): 빈 이름으로
스프링 컨테이너에서 스프링 빈을 찾는 가장 기본적인 조회 방법ac.getBean(빈이름, 타입)ac.getBean(타입)조회 대상 스프링 빈이 없으면 예외 발생NoSuchBeanDefinitionException: No bean name 'xxxx' available구
타입으로 조회시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생한다. 이때는 빈 이름을 지정하자.ac.getBeansOfType()을 사용하면 해당 타입의 모든 빈을 조회할 수 있다.

부모 타입으로 조회하면, 자식 타입도 함께 조회한다.그래서 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.

beanFactory와 ApplicationContext에 대해서 알아보자.BeanFactory스프링 컨테이너의 최상위 인터페이스다.스프링 빈을 관리하고 조회하는 역할을 담당한다.getBean()을 제공한다.지금까지 우리가 사용했던 대부분의 기능은 BeanFactory

스프링 컨테이너는 다양한 형식의 설정 정보를 받아드릴 수 있게 유연하게 설계되어 있다.자바 코드, XML, Groovy 등애노테이션 기반 자바 코드 설정 사용지금까지 했던 것이다.new AnnotationConfigApplicationContext(AppConfig.c

스프링은 어떻게 이렇게 다양한 설정 형식을 지원하는 것일까? 그 중심에는 BeanDefinition 이라는 추상화가 있다.쉽게 이야기해서 역할과 구현을 개념적으로 나눈 것이다.XML을 읽어서 BeanDefinition을 만들면 된다.자바 코드를 읽어서 BeanDefin

스프링은 태생이 기업용 온라인 서비스 기술을 지원하기 위해 탄생했다.대부분의 스프링 애플리케이션은 웹 애플리케이션이다. 물론 웹이 아닌 애플리케이션 개발도 얼마든지 할 수 있다.웹 애플리케이션은 보통 여러 고객이 동시에 요청을 한다.스프링 없는 순수한 DI 컨테이너 테
싱글톤 패턴클래스 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.private 생성자를 사용해서 외부에서 임으로 new 키워드를 사용하지 못하도록 막아야 한다.싱글톤 패턴을 적용한 예제 코드를 보자

싱글톤 컨테이너스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤 (1개만 생성)으로 관리한다. 지금까지 우리가 학습한 스프링 빈이 바로 싱글톤으로 관리되는 빈이다.싱글톤 컨테이너스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를
싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지 (stateful)하게 설계하면 안된다.무상태(stateless)
@Configuration과 싱글톤이상한 점이 있다. AppConfig 코드를 보자.memberService 빈을 만드는 코드를 보면 memberRepository()를 호출한다.이 메서드를 호출하면 new MemoryMemberRepository()를 호출한다.ord

스프링 컨테이너는 싱글톤 레지스트리다. 따라서 스프링 빈이 싱글톤이 되도록 보장해주어여 한다. 그런데 스프링이 자바 코드까지 어떻게 하기는 어렵다. 저 자바 코드를 보면 분명 3번 호출되어야 하는 것이 맞다.그래서 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사

지금까지 스프링 빈을 등록할 때는 자바 코드의 @Bean이나 XML의 <bean> 등을 통해서 설정 정보에 직접 등록할 스프링 빈을 나열했다.예제에서는 몇개가 안되었지만, 이렇게 등록해야 할 스프링 빈이 많아지면 번거럽고 누락하는 문제도 발생한다.그래서 스프링은
탐색할 패키지의 시작 위치 지정모든 자바 클래스를 다 컴포넌트 스캔하면 시간이 오래 걸린다. 그래서 꼭 필요한 위치부터 탐색하도록 시작 위치를 지정할 수 있다.basePackages: 탐색할 패키지의 시작 위치를 지정한다. 이 패키지를 포함해서 하위 패키지를 모두 탐색
includeFilters: 컴포넌트 스캔 대상을 추가로 지정한다.excludeFilters: 컴포넌트 스캔에서 제외할 대상을 지정한다.컴포넌트 스캔 대상에 추가할 애노테이션컴포넌트 스캔 대상에 제외할 애노테이션컴포넌트 스캔 대상에 추가할 클래스@MyIncludeCom
컴포넌트 스캔에서 같은 빈 이름을 등록하면 어떻게 될까?두 가지 상황이 있다.자동 빈 등록 vs 자동 빈 등록수동 빈 등록 vs 자동 빈 드옥자동 빈 등록 vs 자동 빈 등록컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록되는데, 그 이름이 같은 경우 스프링은 오류를 발
의존관계 주입은 크게 4가지 방법이 있다.생성자 주입수정자 주입(setter 주입)필드 주입일반 메서드 주입이름 그대로 생성자를 통해서 의존 관계를 주입 받는 방법이다.지금까지 우리가 진행했던 방법이 바로 생성자 주입이다.특징생성자 호출시점에 딱 1번만 호출되는것이 보
과거에는 수정자 주입과 필드 주입을 많이 사용했지만, 최근에는 스프링을 포함안 DI 프레임워크 대부분이 생성자 주입을 권장한다. 그 이유는 다음과 같다.불변대부분의 의존관계 주입은 한 번 일어나면 애플리케이션 종료시점까지 의존관계를 변경할 일이 없다. 오히려 대부분의
막상 개발을 해보면, 대부분이 다 불변이고, 생성자에 final 키워드를 사용하게 된다. 그런데 생성자도 만들어야하고,
@Autowired는 타입으로 조회한다.타입으로 조회하기 때문에, 마치 다음 코드와 유사하게 동작한다.스프링 빈 조회에서 학습했듯이 타입으로 조회하면 선택된 빈이 2개 이상일 때 문제가 발생한다.DiscountPolicy의 하위 타입인 FixDiscountPolicy,
조회 대상 빈이 2개 이상일 때 해결 방법Autowired 필드 명 매칭@Qualifier -> @Qualifier끼리 매칭 -> 빈 이름 매칭@Primary 사용Autowired 필드 명 매칭@Autowired는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이
Qualifier("mainDiscountPolicy") 이렇게 문자를 먹으면 컴파일시 타입 체크가 안된다. 다음과 같은 애노테이션을 만들어서 문제를 해결할 수 있다.애노테이션에는 상속이라는 개념이 없다. 이렇게 여러 애노테이션들을 모아서 사용하는 기능은 스프링이 지원
해당 타입의 스프링 빈이 모두 필요한 경우도 있다.예를 들어 할인 서비스를 제공하는데, 클라이언트가 할인의 종류(rate, fix)를 선택할 수 있다고 가정해보자. 스프링을 사용하면 소위 말하는 전략 패턴을 매우 간단하게 구현할 수 있다.로직 분석DiscountServ
편리한 자동 기능을 기본으로 사용하자그러면 어떤 경우에 컴포넌트 스캔과 자동 주입을 사용하고, 어떤 경우에 설정 정보를 통해서 수동으로 빈을 등록하고 의존관계를 주입할까?결론부터 이야기하면, 스프링이 나오고 시간이 지날수록 자동을 선호하는 추세다. @Component뿐
코드를 바로 보자InitializingBean은 afterPropertiesSet()메서드로 초기화를 지원한다.DisposableBean은 destory()메서드로 소멸을 지원한다.출력 결과출력 결과를 보면 초기화 메서드가 주입 완료 후에 적절하게 호출 된 것을 확인할
설정 정보에 @Bean(initMethod = "init", destroyMethod = "close") 처럼 초기화, 소멸 메서드를 지정할 수 있다.설정 정보를 사용하도록 변경설정 정보에 초기화 소멸 메서드 지정결과설정 정보 사용 특징메서드 이름을 자유롭게 줄 수 있
우선 코드를 먼저 보자.실행 결과@PostConstruct, @PreDestory 이 두 애노테이션을 사용하면 가장 편리하게 초기화와 종료를 실행할 수 있다.@PostConstruct, @PreDestory 애노테이션 특징최신 스프링에서 가장 권장하는 방법이다.애노테이
지금까지 우리는 스프링 빈이 스프링 컨테이너의 시작과 함께 생성되어서 스프링 컨테이너가 종료될 때 까지 유지된다고 학습했다. 이것은 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 때문이다. 스코프는 번역 그대로 빈이 존재할 수 있는 범위를 뜻한다.스프링은 다음과 같은

싱글톤 스코프의 빈을 조회하면 스프링 컨테이너는 항상 같은 인스턴스의 스프링 빈을 반환한다. 반면에 프로토타입 스코프를 스프링 컨테이너에 조회하면 항상 새로운 인스턴스를 생성해서 반환한다.싱글톤 스코프의 빈을 스프링 컨테이너에 요청한다.스프링 컨테이너는 본인이 관리하는

스프링 컨테이너에 프로토타입 스코프의 빈을 요청하면 항상 새로운 객체 인스턴스를 생성해서 반환한다. 하지만 싱글톤 빈과 함께 사용할 때는 의도한 대로 잘 동작하지 않으므로 주의해야 한다.스프링 컨테이너에 프로토타입 빈을 직접 요청하는 예제를 보자.프로토타입 빈 직접 요
싱글톤 빈과 프로토타입 빈을 함께 사용할 때, 어떻게하면 사용할 때 마다 항상 새로운 프로토타입 빈을 생성할 수 있을까?스프링 컨테이너에 요청가장 간단한 방법은 싱글톤 빈이 프로토타입을 사용할 때 마다 스프링 컨테이너에 새로 요청하는 것이다.실행해보면 ac.getBea

지금까지 싱글톤과 프로토타입 스코프를 학습했다. 싱글톤은 스프링 컨테이너의 시작과 끝까지 함께하는 매우 긴 스코프이고, 프로토타입은 생성과 의존관계 주입, 그리고 초기화까지만 진행하는 특별한 스코프이다.이번에는 웹 스코프에 대해서 알아보자웹 스코프의 특징웹 스코프는 웹
웹 환경 추가웹 스코프는 웹 환경에서만 동작하므로 web 환경이 동작하도록 라이브러리를 추가해야 한다.build.gradle에 추가이제 hello.core.CoreApplication 의 main 메서드를 실행하면 웹 애플리케이션이 실행되는 것을 확인할 수 있다.참고:
첫번째 해결방안은 앞서 배운 Provider를 사용하는 것이다.간단히 ObjectProvider를 사용해보자.main() 메서드로 스프링을 실행하고, 웹 브라우저에 http://localhost:8080/log-demo 를 입력하자.잘 작동한다.ObjectP

이번에는 프록시 방식을 사용해보자.여기가 핵심이다. proxyMode = ScopedProxyMode.TARGET_CLASS 를 추가해주자. 적용 대상이 인터페이스가 아닌 클래스면 TARGET_CLASS 를 선택적용 대상이 인터페이스면 INTERFACES 를 선택이렇게
데이터베이스 커넥션 풀이나, 네트워크 소켓처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면, 객체의 초기화의 종료 작업이 필요하다. 스프링을 통해 이러한 초기화 작업과 종료 작업을 어떻게 진행하는

모든 데이터는 HTTP를 기반으로 주고 받는다.웹 서버는 정적 리소스(파일), WAS는 애플리케이션 로직사실은 둘의 용어도 경계도 모호함웹 서버도 프로그램을 실행하는 기능을 포함하기도 함웹 애플리케이션 서버도 웹 서버의 기능을 제공함자바는 서블릿 컨테이너 기능을 제공하

아래와 같은 폼이 있을때 전송을 누르면, 웹 브라우저가 HTTP 메시지를 만든다.서버를 처음부터 직접 구현하려면 복잡하고 힘들다.그래서 서블릿이 나온다.서블릿이 앞 뒤 과정을 다 지원한다.urlPatterns(/hello)의 URL이 호출되면 서블릿 코드가 실행HTTP

클라이언트에서 서버로 요청을 하면 서블릿을 호출한다.그런데 서블릿 객체는 누가 호출할까?애플리케이션 코드를 하나하나 순차적으로 실행하는 것은 쓰레드자바 메인 메서드를 처음 실행하면 main이라는 이름의 쓰레드가 실행쓰레드가 없다면 자바 애플리케이션 실행이 불가능쓰레드는

고정된 HTML 파일, CSS, JS 이미지, 영상 등을 제공주로 웹 브라우저동적으로 필요한 HTML 파일을 생성해서 전달웹 브라우저: HTML 해석HTML이 아니라 데이터를 전달주로 JSON 형식 사용다양한 시스템에서 호출데이터만 주고 받음, UI 화면이 필요하면,
서블릿 - 1997HTML 생성이 어려움JSP - 1999HTML 생성은 편리하지만, 비즈니스 로직까지 너무 많은 역할 담당서블릿, JSP 조합 MVC 패턴 사용모델, 뷰, 컨트롤러로 역할을 나누어 개발MVC 프레임워크 춘추 전국 시대 - 2000년 초 ~ 2010년

https://start.spring.io/프로젝트 선택Project: Gradle - Groovy ProjectLanguage: JavaSpring Boot: 2.7.xProject MetadataGroup: helloArtifact: servletName:

스프링 부트 환경에서 서블릿을 등록하고 사용해보자참고서블릿은 톰캣 같은 웹 애플리케이션 서버를 직접 설치하고, 그 위에 서블릿 코드를 클래스 파일로 빌드해서 올린 다음, 톰캣 서버를 실행하면 된다. 하지만 이 과정은 매우 번거롭다.스프링 부트는 톰캣 서버를 내장하고 있
HTTP 요청 메시지를 개발자기 직접 파싱해서 사용해도 되지만, 매우 불편할것이다. 서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱한다. 그리고 결과를 HttpServletRequest 객체에 담아서 제공한
HttpServletRequest가 제공하는 기본 기능들을 알아보자.참고로컬에서 테스트하면 IPv6 정보가 나오는데, IPv4 정보를 보고 싶으면 다음 옵션을 VM options에 넣어주면 된다.\-Djava.net.preferIPv4Stack=true

HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법을 알아보자.주로 다음 3가지 방법을 사용한다.GET - 쿼리 파라미터/url?username=?hello&age=20메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달예) 검색,
다음 데이터를 클라이언트에서 서버로 전송해보자.전달 데이터username=helloage=20메시지 바디 없이, URL의 쿼리 파라미터를 사용해서 데이터를 전달하자.예)검색, 필터, 페이징등에서 많이 사용하는 방식쿼리 파라미터는 URL에 다음과 같이 ?를 시작으로 보낼
이번에는 HTML의 Form을 사용해서 클라이언트에서 서버로 데이터를 전송해보자.주로 회원가입, 상품 주문 등에서 사용하는 방식이다.특징content-type: application/x-www-form-urlencoded메시지 바디에 쿼리 파리미터 형식으로 데이터를 전
HTTP message body에 데이터를 직접 담아서 요청HTTP API에서 주로 사용, JSON, XML, TEXT데이터 형식은 주로 JSON 사용POST, PUT, PATCH먼저 가장 단순한 텍스트 메시즈를 HTTP 메시지 바디에 담아서 전송하고, 읽어보자.HTT
이번에는 HTTP API에서 주로 사용하는 JSON 형식으로 데이터를 전달해보자.JSON 형식 전송POST http://localhost:8080/request-body-jsoncontent-type: application/jsonmessage body: {"
HttpServletResponse 역할HTTP 응답 메시지 작성HTTP 응답코드 지정헤더 생성바디 생성편의 기능 제공Content-Type, 쿠키, RedirectHttpServletResponse - 기본 사용법hello.servlet.basic.response.R
HTTP 응답 메시지는 주로 다음 내용을 담아서 전달한다.단순 텍스트 응답writer.println("ok")HTML 응답HTTP API - Message Body Json 응답HttpServletResponse - HTML 응답hello.servlet.web.resp
hello.servlet.web.response. ResponseJsonServletHTTP 응답으로 JSON을 반환할 때는 content-type을 application/json 로 지정해야 한다.Jackson 라이브러리가 제공하는 objectMapper.writeV
회원 관리 웹 애플리케이션 요구사항회원 정보이름: username나이: age기능 요구사항회원 저장회원 목록 조회회원 도메인 모델id는 Member를 회원 저장소에 저장하면 회원 저장소가 할당한다.회원 저장소회원 저장소는 싱글톤 패턴을 적용했다. 스프링을 사용하면 스프
서블릿으로 회원 관리 웹 애플리케이션을 만들어보자.가장 먼저 서블릿으로 회원 등록 HTML 폼을 제공해보자.MemberFormServlet - 회원 등록 폼MemberFormServlet 은 단순하게 회원 정보를 입력할 수 있는 HTML Form을 만들어서 응답한다.
JSP를 사용하려면 먼저 다음 라이브러리를 추가해야 한다.build.gradle에 추가회원 등록 폼 JSPmain/webapp/jsp/members/new-form.jsp<%@ page contentType="text/html;charset=UTF-8" langu

너무 많은 역할하나의 서블릿이나 JSP만으로 비즈니스 로직과 뷰 렌더링까지 모두 처리하게 되면, 너무 많은 역할을 하게되고 유지보수가 어려워진다. 비즈니스 로직을 수정해야해도 해당 코드를 손대야하고 UI를 변경할때도 해당 파일을 수정해야 한다.변경 라이프 사이클진짜 문
서블릿을 컨트롤러로 사용하고, JSP를 뷰로 사용해서 MVC 패턴을 적용해보자.Model은 HttpServletRequest 객체를 사용한다. request는 내부에 데이터 저장소를 가지고 있는데,request.setAttribute() , request.getAttr

프론트 컨트롤러 도입 전은 공통 로직이 필요하더라도 공통 로직을 호출마다 각각 다 만들어야한다.하지만 도입 후에는 공통 로직을 한군데 몰아놓고 사용할 수 있다.FrontController 패턴 특징프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음프론트 컨트롤러가

프론트 컨트롤러를 단계적으로 도입해보자.기존 코드를 최대한 유지하면서, 프론트 컨트롤러를 도입하는 것이다.먼저 구조를 맞추어두고 점진적으로 리팩터링 해보자.V1구조ControllerV1서블릿과 비슷한 모양의 컨트롤러 인터페이스를 도입한다. 각 컨트롤러들은 이 인터페이스

모든 컨트롤러에서 뷰로 이동하는 부분에 중복이 있고, 깔끔하지 않다.이 부분을 깔끔하게 분리하기 위해 별도로 뷰를 처리하는 객체를 만들자.MyView뷰 객체는 이후 다른 버전에서도 함께 사용하므로 패키지 위치를 frontcontroller 에 두었다.이 코드만 봐서는

서블릿 종속성 제거컨트롤러 입장에서 HttpServletRequest, HttpServletResponse 가 꼭 필요할까?요청 파라미터 정보는 자바의 Map으로 대신 넘기도록 하면 지금 구조에서는 컨트롤러가 서블릿 기술을 몰라도 동작할 수 있다.그리고 request

앞서 만든 v3 컨트롤러는 서블릿 종속성을 제거하고 뷰 경로의 중복을 제거하는 등, 잘 설계된 컨트롤러이다. 그런데 실제 컨트톨러 인터페이스를 구현하는 개발자 입장에서 보면, 항상 ModelView 객체를 생성하고 반환해야 하는 부분이 조금은 번거롭다.좋은 프레임워크는

만약 어떤 개발자는 ControllerV3 방식으로 개발하고 싶고, 어떤 개발자는 ControllerV4 방식으로 개발하고 싶다면 어떻게 해야할까?어댑터 패턴지금까지 우리가 개발한 프론트 컨트롤러는 한가지 방식의 컨트롤러 인터페이스만 사용할 수 있다.Controller
FrontControllerServletV5 에 ControllerV4 기능도 추가해보자.핸들러 매핑( handlerMappingMap )에 ControllerV4 를 사용하는 컨트롤러를 추가하고, 해당 컨트롤러를 처리할 수 있는 어댑터인 ControllerV4Hand

직접 만든 MVC 프레임워크와 스프링 MVC를 비교해보자.직접 만든 프레임워크 스프링 MVC 비교FrontController -> DispatcherServlethandlerMappingMap -> HandlerMappingMyHandlerAdapter -> Handl

핸들러 매핑과 핸들러 어댑터가 어떤 것들이 어떻게 사용되는지 알아보자.지금은 전혀 사용하지 않지만, 과거에 주로 사용했던 스프링이 제공하는 간단한 컨트롤러로 핸들러 매핑과 어댑터를 이해해보자.과거 버전 스프링 컨트롤러org.springframework.web.servl

이번에는 뷰 리졸버에 대해서 자세히 알아보자.OldController - View 조회할 수 있도록 변경view를 사용할 수 있도록 코드를 추가하였다.실행http://localhost:8080/springmvc/old-controller웹 브라우저에 White
스프링이 제공하는 컨트롤러는 애노테이션 기반으로 동작해서, 매우 유연하고 실용적이다. 과거에는 자바 언어에 애노테이션이 없기도 했고, 스프링도 처음부터 이런 유연한 컨트롤러를 제공한 것은 아니다.@RequestMapping스프링은 애노테이션을 활용한 매우 유연하고 실용

@RequestMapping 을 잘 보면 클래스 단위가 아니라 메서드 단위에 적용된 것을 확인할 수 있다. 따라서 컨트롤러 클래스를 유연하게 하나로 통합할 수 있다.SpringMemberControllerV2컨트롤러 클래스를 통합하는 것을 넘어서 조합도 가능하다.다음
MVC 프레임워크 만들기에서 v3은 ModelView를 개발자가 직접 생성해서 반환했기 때문에, 불편했던 기억이 날 것이다. 물론 v4를 만들면서 실용적으로 개선한 기억도 날 것이다.스프링 MVC는 개발자가 편리하게 개발할 수 있도록 수 많은 편의 기능을 제공한다.실무
스프링 부트 스타터 사이트로 이동해서 스프링 프로젝트 생성https://start.spring.io프로젝트 선택Project: Gradle ProjectLanguage: JavaSpring Boot: 2.4.xProject MetadataGroup: hello

앞으로 로그를 사용할 것이기 때문에, 이번시간에는 로그에 대해서 간단히 알아보자.운영 시스템에서는 System.out.println() 같은 시스템 콘솔을 사용해서 필요한 정보를 출력하지 않고,별도의 로깅 라이브러리를 사용해서 로그를 출력한다.참고로 로그 관련 라이브러

MappingController매핑 정보(한번 더)@RestController@Controller 는 반환 값이 String 이면 뷰 이름으로 인식된다. 그래서 뷰를 찾고 뷰가 랜더링 된다.@RestController 는 반환 값으로 뷰를 찾는 것이 아니라, HTTP
회원 관리를 HTTP API로 만든다 생각하고 매핑을 어떻게 하는지 알아보자.(실제 데이터가 넘어가는 부분은 생략하고 URL 매핑만)회원 관리 API회원 목록 조회: GET /users회원 등록: POST /users회원 조회: GET /users/{userId}회원
애노테이션 기반의 스프링 컨트롤러는 다양한 파라미터를 지원한다.이번 시간에는 HTTP 헤더 정보를 조회하는 방법을 알아보자.RequestHeaderControllerHttpServletResponseHttpMethod : HTTP 메서드를 조회한다. org.spring

서블릿에서 학습했던 HTTP 요청 데이터를 조회 하는 방법을 다시 떠올려보자. 그리고 서블릿으로 학습했던 내용을 스프링이 얼마나 깔끔하고 효율적으로 바꾸어주는지 알아보자.클라이언트에서 서버로 요청 데이터를 전달할 때는 주로 다음 3가지 방법을 사용한다.RequestPa
스프링이 제공하는 @RequestParam 을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다.requestParamV2@RequestParam : 파라미터 이름으로 바인딩@ResponseBody : View 조회를 무시하고, HTTP message body에
실제 개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어주어야 한다. 보통 다음과 같이 코드를 작성할 것이다.스프링은 이 과정을 완전히 자동화해주는 @ModelAttribute 기능을 제공한다.먼저 요청 파라미터를 바인딩 받을 객체를 만들자.
HTTP message body에 데이터를 직접 담아서 요청HTTP API에서 주로 사용, JSON, XML, TEXT데이터 형식은 주로 JSON 사용POST, PUT, PATCH요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우는 @Re
이번에는 HTTP API에서 주로 사용하는 JSON 데이터 형식을 조회해보자.RequestBodyJsonControllerHttpServletRequest를 사용해서 직접 HTTP 메시지 바디에서 데이터를 읽어와서, 문자로 변환한다.문자로 된 JSON 데이터를 Jack
응답 데이터는 이미 앞에서 일부 다룬 내용들이지만, 응답 부분에 초점을 맞추어서 정리해보자.스프링(서버)에서 응답 데이터를 만드는 방법은 크게 3가지이다.정적 리소스예) 웹 브라우저에 정적인 HTML, css, js를 제공할 때는, 정적 리소스를 사용한다.뷰 템플릿 사
HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 하므로, HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 실어 보낸다.HTTP 요청에서 응답까지 대부분 다루었으므로 이번시간에는 정리를 해보자.참고HTML이나 뷰 템플릿을 사용해도 HTTP

템플릿으로 HTML을 생성해서 응답하는 것이 아니라, HTTP API처럼 JSON 데이터를 HTTP 메시지 바디에서 직접 읽거나 쓰는 경우 HTTP 메시지 컨버터를 사용하면 편리하다. @ResponseBody 를 사용HTTP의 BODY에 문자 내용을 직접 반환view

HTTP 메시지 컨버터는 스프링 MVC 어디쯤에서 사용되는 것일까?애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있었다.애노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdapter 는 바로 이 ArgumentResolver 를

스프링 부트 스타터 사이트로 이동해서 스프링 프로젝트 생성https://start.spring.iobuild.gradleWelcome 페이지 추가편리하게 사용할 수 있도록 Welcome 페이지를 추가하자./resources/static/index.html

상품을 관리할 수 있는 서비스를 만들어보자.상품 도메인 모델상품 ID상품명가격수량상품 관리 기능상품 목록상품 상세상품 등록상품 수정
Item - 상품 객체ItemRepository - 상품 저장소ItemRepositoryTest - 상품 저장소 테스트
핵심 비즈니스 로직을 개발하는 동안, 웹 퍼블리셔는 HTML 마크업을 완료했다.다음 파일들을 경로에 넣고 잘 동작하는지 확인해보자.HTML, css 파일/resources/static/css/bootstrap.min.css 부트스트랩 다운로드/resources/stat

본격적으로 컨트롤러와 뷰 템플릿을 개발해보자BasicItemController컨트롤러 로직은 itemRepository에서 모든 상품을 조회한 다음에 모델에 담는다. 그리고 뷰 템플릿을 호출한다.테스트용 데이터 추가테스트용 데이터가 없으면 회원 목록 기능이 정상 동작하
상품 상세 컨트롤러와 뷰를 개발하자.BasicItemController에 추가PathVariable 로 넘어온 상품ID로 상품을 조회하고, 모델에 담아둔다. 그리고 뷰 템플릿을 호출한다.상품 상세 뷰정적 HTML을 뷰 템플릿(templates) 영역으로 복사하고 다음과
상품 등록 폼BasicItemController에 추가상품 등록 폼은 단순히 뷰 템플릿만 호출한다.상품 등록 폼 뷰정적 HTML을 뷰 템플릿(templates) 영역으로 복사하고 다음과 같이 수정하자./resources/templates/basic/addForm.htm
이제 상품 등록 폼에서 전달된 데이터로 실제 상품을 등록 처리해보자.상품 등록 폼은 다음 방식으로 서버에 데이터를 전달한다.상품 등록 처리 - @RequestParamaddItemV1 - BasicItemController에 추가먼저 @RequestParam Strin
상품 수정 폼 컨트롤러BasicItemController에 추가수정에 필요한 정보를 조회하고, 수정용 폼 뷰를 호출한다.상품 수정 폼 뷰정적 HTML을 뷰 템플릿(templates) 영역으로 복사하고 다음과 같이 수정하자./resources/templates/basic

사실 지금까지 진행한 상품 등록 처리 컨트롤러는 심각한 문제가 있다. (addItemV1 ~ addItemV4)상품 등록을 완료하고 웹 브라우저의 새로고침 버튼을 클릭해보자.상품이 계속해서 중복 등록되는 것을 확인할 수 있다.POST 등록 후 새로 고침웹 브라우저의 새
상품을 저장하고 상품 상세 화면으로 리다이렉트 한 것 까지는 좋았다.저장이 잘 되었으면 상품 상세 화면에"저장되었습니다"라는 메시지를 보여달라는 요구사항이 왔다.BasicItemController에 추가리다이렉트 할 때 간단히 status=true 를 추가해보자. 그리

스프링 부트 스타터 사이트로 이동해서 스프링 프로젝트 생성https://start.spring.iobuild.gradle홈 화면/resources/static/index.htmlIntelliJ Gradle 대신에 자바 직접 실행최근 IntelliJ 버전은 Gr
타임리프는 기본적으로 HTML 테그의 속성에 기능을 정의해서 동작한다. HTML의 콘텐츠(content)에데이터를 출력할 때는 다음과 같이 th:text 를 사용하면 된다.<span th:text="${data}">HTML 테그의 속성이 아니라 HTML 콘텐츠 영
타임리프에서 변수를 사용할 때는 변수 표현식을 사용한다.변수 표현식 : ${...}그리고 이 변수 표현식에는 스프링 EL이라는 스프링이 제공하는 표현식을 사용할 수 있다.BasicController 추가/resources/templates/basic/variable.h
타임리프는 기본 객체들을 제공한다.${- ${- ${- ${- ${그런데 이런 점을 해결하기 위해 편의 객체도 제공한다.HTTP 요청 파라미터 접근: param예) ${param.paramData}HTTP 세션 접근: session예) ${session.sessionD
타임리프는 문자, 숫자, 날짜, URI등을 편리하게 다루는 다양한 유틸리티 객체들을 제공한다.타임리프 유틸리티 객체들타임리프 유틸리티 객체https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html유틸리티
타임리프에서 URL을 생성할 때는 @{...} 문법을 사용하면 된다.BasicController 추가/resources/templates/basic/link.html단순한 URL@{/hello} /hello쿼리 파라미터@{/hello(param1=${param1}, p
리터럴은 소스 코드상에 고정된 값을 말하는 용어이다.예를 들어서 다음 코드에서 "Hello" 는 문자 리터럴, 10 , 20 는 숫자 리터럴이다.타임리프는 다음과 같은 리터럴이 있다.문자: 'hello'숫자: 10불린: true , falsenull: null타임리프에
타임리프 연산은 자바와 크게 다르지 않다. HTML안에서 사용하기 때문에 HTML 엔티티를 사용하는 부분만 주의하자.BasicController 추가/resources/templates/basic/operation.html비교연산: HTML 엔티티를 사용해야 하는 부분
타임리프는 주로 HTML 태그에 th: 속성을 지정하는 방식으로 동작한다. th: 로 속성을 적용하면 기존 속성을 대체한다. 기존 속성이 없으면 새로 만든다.BasicController 추가/resources/templates/basic/attribute.html속성
타임리프에서 반복은 th:each 를 사용한다. 추가로 반복에서 사용할 수 있는 여러 상태 값을 지원한다.BasicController 추가/resources/templates/basic/each.html반복 기능<tr th:each="user : ${users}"
타임리프의 조건식if , unless ( if 의 반대)BasicController 추가/resources/templates/basic/condition.htmlif, unless타임리프는 해당 조건이 맞지 않으면 태그 자체를 렌더링하지 않는다.만약 다음 조건이 fal
BasicController 추가/resources/templates/basic/comments.html결과1\. 표준 HTML 주석자바스크립트의 표준 HTML 주석은 타임리프가 렌더링 하지 않고, 그대로 남겨둔다.2\. 타임리프 파서 주석타임리프 파서 주석은 타임리프
<th:block> 은 HTML 태그가 아닌 타임리프의 유일한 자체 태그다.BaiscController 추가/resources/templates/basic/block.html실행결과타임리프의 특성상 HTML 태그안에 속성으로 기능을 정의해서 사용하는데, 위 예처럼
타임리프는 자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.자바스크립트 인라인 기능은 다음과 같이 적용하면 된다.<script th:inline="javascript">BasicController 추가/resources/te
웹 페이지를 개발할 때는 공통 영역이 많이 있다.헤더, 푸터 등등이런 부분을 코드를 복사해서 사용한다면 변경시 여러페이지를 다 수정해야 하므로 상당히 비효율 적이다. 타임리프는 이런 문제를 해결하기 위해 템플릿 조각과 레이아웃 기능을 지원한다.템플릿 조각/resourc
이전에는 일부 코드 조각을 가지고와서 사용했다면, 이번에는 개념을 더 확장해서 코드 조각을 레이아웃에 넘겨서 사용하는 방법에 대해서 알아보자./resources/templates/template/layout/base.html/resources/templates/temp
앞서 이야기한 개념을 <head> 정도에만 적용하는게 아니라 <html> 전체에 적용할 수도 있다./resources/templates/template/layoutExtend/layoutFile.html/resources/templates/template/l
2-1은 지난 강의에서 학습한 상품 관리 프로젝트를 임포트하는 과정이였다.타임리프는 크게 2가지 메뉴얼을 제공한다.기본 메뉴얼: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html스프링 통합 메뉴얼
지금부터 타임리프가 제공하는 입력 폼 기능을 적용해서 기존 프로젝트의 폼 코드를 타임리프가 지원하는 기능을 사용해서 효율적으로 개선해보자.th:object : 커맨드 객체를 지정한다.\*{...} : 선택 변수 식이라고 한다. th:object 에서 선택한 객체에 접근

판매 여부판매 오픈 여부체크 박스로 선택할 수 있다.등록 지역서울, 부산, 제주체크 박스로 다중 선택할 수 있다.상품 종류도서, 식품, 기타라디오 버튼으로 하나만 선택할 수 있다.배송 방식빠른 배송일반 배송느린 배송셀렉트 박스로 하나만 선택할 수 있다.ItemType
단순 HTML 체크 박스resources/templates/form/addForm.html 추가상품이 등록되는 곳에 다음과 같이 로그를 남겨서 값이 잘 넘어오는지 확인해보자.FormItemController 추가FormItemController 에 @Slf4j 애노테이
개발할 때 마다 이렇게 히든 필드를 추가하는 것은 상당히 번거롭다. 타임리프가 제공하는 폼 기능을 사용하면 이런 부분을 자동으로 처리할 수 있다.타임리프 - 체크 박스 코드 추가체크 박스의 기존 코드를 제거하고 타임리프가 제공하는 체크 박스 코드로 변경하자.타임리프 체
체크 박스를 멀티로 사용해서, 하나 이상을 체크할 수 있도록 해보자.등록 지역서울, 부산, 제주체크 박스로 다중 선택할 수 있다.FormItemController - 추가@ModelAttribute의 특별한 사용법등록 폼, 상세화면, 수정 폼에서 모두 서울, 부산, 제
라디오 버튼은 여러 선택지 중에 하나를 선택할 때 사용할 수 있다. 이번시간에는 라디오 버튼을 자바 ENUM을 활용해서 개발해보자.상품 종류도서, 식품, 기타라디오 버튼으로 하나만 선택할 수 있다.FormItemController - 추가itemTypes 를 등록 폼,
셀렉트 박스는 여러 선택지 중에 하나를 선택할 때 사용할 수 있다. 이번시간에는 셀렉트 박스를 자바 객체를 활용해서 개발해보자.배송 방식빠른 배송일반 배송느린 배송셀렉트 박스로 하나만 선택할 수 있다.FormItemController - 추가DeliveryCode 라는
3-1은 프로젝트 임포트였다.기획자가 화면에 보이는 문구가 마음에 들지 않는다고, 상품명이라는 단어를 상품이름으로 바꿔달라고 한다.화면을 하나하나 찾아가서 고쳐도되지만, 효율적이지 못한 방법이다.이런 다양한 메시지를 한 곳에서 관리하도록 하는 기능을 메시지 기능이다.예
스프링은 기본적인 메시지 관리 기능을 제공한다.메시지 관리 기능을 사용하려면 스프링이 제공하는 MessageSource 를 스프링 빈으로 등록하면 되는데, MessageSource 는 인터페이스이다. 따라서 구현체인 ResourceBundleMessageSource 를
MessageSource 인터페이스를 보면 코드를 포함한 일부 파라미터로 메시지를 읽어오는 기능을 제공한다.스프링이 제공하는 메시지 소스를 어떻게 사용하는지 테스트 코드를 통해서 학습해보자.test/java/hello/itemservice/message.MessageS
실제 웹 애플리케이션에 메시지를 적용해보자.먼저 메시지를 추가 등록하자.messages.properties타임리프 메시지 적용타임리프의 메시지 표현식 예를 들어서 방금 등록한 상품이라는 이름을 조회하려면 타임리프 템플릿 파일에 메시지를 적용해보자addForm.html페
이번에는 웹 애플리케이션에 국제화를 적용해보자. 먼저 영어 메시지를 추가하자.messages_en.properties사실 이것으로 국제화 작업은 거의 끝났다. 앞에서 템플릿 파일에는 모두 웹브라우저에서 설정 언어를 영어로 바꾸면 잘적용되는것을 확인할 수 있다.스프링의
상품 관리 시스템에 새로운 요구사항이 추가되었다.요구사항: 검증 로직 추가타입 검증가격, 수량에 문자가 들어가면 검증 오류 처리필드 검증상품명: 필수, 공백X가격: 1000원 이상, 1백만원 이하수량: 최대 9999특정 필드의 범위를 넘어서는 검증가격 \* 수량의 합은

4-2는 프로젝트 임포트였다.사용자가 상품 등록 폼에서 정상 범위의 데이터를 입력하면, 서버에서는 검증 로직이 통과하고, 상품을 저장하고, 상품 상세 화면으로 redirect한다.객이 상품 등록 폼에서 상품명을 입력하지 않거나, 가격, 수량 등이 너무 작거나 커서 검증
먼저 상품 등록 검증 코드를 작성해보자.ValidationItemControllerV1 - addItem() 수정검증 오류 보관Map<String, String> errors = new HashMap<>();만약 검증시 오류가 발생하면 어떤 검증에서 오류가
4-5는 프로젝트 준비 과정이였다.지금부터 스프링이 제공하는 검증 오류 처리 방법을 알아보자. 여기서 핵심은 BindingResult이다. 우선 코드로 확인해보자.ValidationItemControllerV2 - addItemV1BindingResult binding
스프링이 제공하는 검증 오류를 보관하는 객체이다. 검증 오류가 발생하면 여기에 보관하면 된다.BindingResult 가 있으면 @ModelAttribute 에 데이터 바인딩 시 오류가 발생해도 컨트롤러가 호출된다!예) @ModelAttribute에 바인딩 시 타입 오
목표사용자 입력 오류 메시지가 화면에 남도록 하자.예) 가격을 1000원 미만으로 설정시 입력한 값이 남아있어야 한다.FieldError , ObjectError 에 대해서 더 자세히 알아보자.ValidationItemControllerV2 - addItemV2Fiel
FieldError 생성자FieldError 는 두 가지 생성자를 제공한다.파라미터 목록objectName : 오류가 발생한 객체 이름field : 오류 필드rejectedValue : 사용자가 입력한 값(거절된 값)bindingFailure : 타입 오류 같은 바인딩
컨트롤러에서 BindingResult 는 검증해야 할 객체인 target 바로 다음에 온다. 따라서 BindingResult 는 이미 본인이 검증해야 할 객체인 target 을 알고 있다.BindingResult 가 제공하는 rejectValue() , reject()
오류 코드를 만들 때 다음과 같이 자세히 만들 수도 있고,required.item.itemName : 상품 이름은 필수 입니다.range.item.price : 상품의 가격 범위 오류 입니다.또는 다음과 같이 단순하게 만들 수도 있다.required : 필수 값 입니다
우선 테스트 코드로 MessageCodesResolver를 알아보자.MessageCodesResolverMessageCodesResolver검증 오류 코드로 메시지 코드들을 생성한다.MessageCodesResolver 인터페이스이고 DefaultMessageCodes
오류 코드 관리 전략핵심은 구체적인 것에서! 덜 구체적인 것으로!MessageCodesResolver 는 required.item.itemName 처럼 구체적인 것을 먼저 만들어주고,required 처럼 덜 구체적인 것을 가장 나중에 만든다.이렇게 하면 앞서 말한 것
스프링이 직접 만든 오류 메시지 처리검증 오류 코드는 다음과 같이 2가지로 나눌 수 있다.개발자가 직접 설정한 오류 코드 rejectValue() 를 직접 호출스프링이 직접 검증 오류에 추가한 경우(주로 타입 정보가 맞지 않음)price 필드에 문자 "A"를 입력해보자
스프링이 Validator 인터페이스를 별도로 제공하는 이유는 체계적으로 검증 기능을 도입하기 위해서다. 그런데 앞에서는 검증기를 직접 불러서 사용했고, 이렇게 사용해도 된다. 그런데 Validator 인터페이스를사용해서 검증기를 만들면 스프링의 추가적인 도움을 받을
검증 기능을 지금처럼 매번 코드로 작성하는 것은 상당히 번거롭다. 특히 특정 필드에 대한 검증 로직은 대부분 빈 값인지 아닌지, 특정 크기를 넘는지 아닌지와 같이 매우 일반적인 로직이다. 다음 코드를 보자.이런 검증 로직을 모든 프로젝트에 적용할 수 있게 공통화하고,
Bean Validation 기능을 어떻게 사용하는지 코드로 알아보자. 먼저 스프링과 통합하지 않고, 순수한 Bean Validation 사용법 부터 테스트 코드로 알아보자.Bean Validation 의존관계 추가의존관계 추가Bean Validation을 사용하려면
5-3은 프로젝트 준비였다.ValidationItemControllerV3 코드 수정참고특정 필드의 범위를 넘어서는 검증(가격 \* 수량의 합은 10,000원 이상) 기능이 빠졌는데, 이 부분은 조금 뒤에 설명한다.스프링 MVC는 어떻게 Bean Validator를 사
Bean Validation이 기본으로 제공하는 오류 메시지를 좀 더 자세히 변경하고 싶으면 어떻게 하면 될까?Bean Validation을 적용하고 bindingResult 에 등록된 검증 오류 코드를 보자. 오류 코드가 애노테이션 이름으로 등록된다. 마치 typeM
Bean Validation에서 특정 필드( FieldError )가 아닌 해당 오브젝트 관련 오류( ObjectError )는 어떻게 처리할 수 있을까?다음과 같이 @ScriptAssert() 를 사용하면 된다.실행해보면 정상 수행되는 것을 확인할 수 있다. 메시지
수정에도 검증 기능을 추가하자ValidationItemControllerV3 - edit() 변경edit() : Item 모델 객체에 @Validated 를 추가하자.검증 오류가 발생하면 editForm 으로 이동하는 코드 추가validation/v3/editForm.
수정시 검증 요구사항데이터를 등록할 때와 수정할 때는 요구사항이 다를 수 있다.등록시 기존 요구사항타입 검증가격, 수량에 문자가 들어가면 검증 오류 처리필드 검증상품명: 필수, 공백X가격: 1000원 이상, 1백만원 이하수량: 최대 9999특정 필드의 범위를 넘어서는
동일한 모델 객체를 등록할 때와 수정할 때 각각 다르게 검증하는 방법을 알아보자.방법 2가지BeanValidation의 groups 기능을 사용한다.Item을 직접 사용하지 않고, ItemSaveForm, ItemUpdateForm 같은 폼 전송을 위한 별도의 모델 객
5-10은 프로젝트 준비였다.실무에서는 groups 를 잘 사용하지 않는데, 그 이유가 다른 곳에 있다. 바로 등록시 폼에서 전달하는 데이터가 Item 도메인 객체와 딱 맞지 않기 때문이다.실무에서는 회원 등록시 회원과 관련된 데이터만 전달받는 것이 아니라, 약관 정보
이제 Item 의 검증은 사용하지 않으므로 검증 코드를 제거해도 된다.ItemSaveForm - ITEM 저장용 폼ItemUpdateForm - ITEM 수정용 폼이제 등록, 수정용 폼 객체를 사용하도록 컨트롤러를 수정하자.ValidationItemControllerV
@Valid , @Validated 는 HttpMessageConverter ( @RequestBody )에도 적용할 수 있다.참고@ModelAttribute 는 HTTP 요청 파라미터(URL 쿼리 스트링, POST Form)를 다룰 때 사용한다.@RequestBody
로그인 요구사항홈 화면 - 로그인 전회원 가입로그인홈 화면 - 로그인 후본인 이름(누구님 환영합니다.)상품 관리로그 아웃보안 요구사항로그인 사용자만 상품에 접근하고, 관리할 수 있음로그인 하지 않은 사용자가 상품 관리에 접근하면 로그인 화면으로 이동회원 가입, 상품 관
패키지 구조 설계package 구조hello.logindomainitemmemberloginwebitemmemberlogin도메인이 가장 중요하다.도메인 = 화면, UI, 기술 인프라 등등의 영역은 제외한 시스템이 구현해야 하는 핵심 비즈니스 업무 영역을 말한다.향후
홈 화면을 개발하자.HomeController - home() 수정templates/home.html 추가
MemberMemberRepositoryMemberController회원 가입 뷰 템플릿templates/members/addMemberForm.html회원용 테스트 데이터 추가편의상 테스트용 회원 데이터를 추가하자.loginId : testpassword : test
로그인 기능을 개발해보자. 지금은 로그인 ID, 비밀번호를 입력하는 부분에 집중하자.LoginService로그인의 핵심 비즈니스 로직은 회원을 조회한 다음에 파라미터로 넘어온 password와 비교해서 같으면 회원을 반환하고, 만약 password가 다르면 null 을
쿠키를 사용해서 로그인, 로그아웃 기능을 구현해보자.쿠키에는 영속 쿠키와 세션 쿠키가 있다.영속 쿠키: 만료 날짜를 입력하면 해당 날짜까지 유지세션 쿠키: 만료 날짜를 생략하면 브라우저 종료시 까지만 유지브라우저 종료시 로그아웃이 되길 기대하므로, 우리에게 필요한 것은
쿠키를 사용해서 로그인Id를 전달해서 로그인을 유지할 수 있었다. 그런데 여기에는 심각한 보안 문제가 있다.보안 문제쿠키 값은 임의로 변경할 수 있다.클라이언트가 쿠키를 강제로 변경하면 다른 사용자가 된다.실제 웹브라우저 개발자모드 Application Cookie 변

세션 동작 방식세션을 어떻게 개발할지 먼저 개념을 이해해보자.사용자가 loginId , password 정보를 전달하면 서버에서 해당 사용자가 맞는지 확인한다.세션 ID를 생성하는데, 추정 불가능해야 한다.UUID는 추정이 불가능하다.생성된 세션 ID와 세션에 보관할
세션 관리는 크게 다음 3가지 기능을 제공하면 된다.세션 생성sessionId 생성 (임의의 추정 불가능한 랜덤 값)세션 저장소에 sessionId와 보관할 값 저장sessionId로 응답 쿠키를 생성해서 클라이언트에 전달세션 조회클라이언트가 요청한 sessionId
지금까지 개발한 세션 관리 기능을 실제 웹 애플리케이션에 적용해보자.LoginController - loginV2()private final SessionManager sessionManager; 주입sessionManager.createSession(loginMemb
서블릿은 세션을 위해 HttpSession 이라는 기능을 제공하는데, 지금까지 나온 문제들을 해결해준다.우리가 직접 구현한 세션의 개념이 이미 구현되어 있고, 더 잘 구현되어 있다.HttpSession 소개서블릿이 제공하는 HttpSession 도 결국 우리가 직접 만
@SessionAttribute스프링은 세션을 더 편리하게 사용할 수 있도록 @SessionAttribute 을 지원한다.이미 로그인 된 사용자를 찾을 때는 다음과 같이 사용하면 된다. 참고로 이 기능은 세션을 생성하지 않는다.@SessionAttribute(name
세션 정보 확인세션이 제공하는 정보들을 확인해보자.SessionInfoControllersessionId : 세션Id, JSESSIONID 의 값이다. 예) 34B14F008AA3527C9F8ED620EFD7A4E1maxInactiveInterval : 세션의 유효 시
공통 관심 사항요구사항을 보면 로그인 한 사용자만 상품 관리 페이지에 들어갈 수 있어야 한다. 하지만 현재는 URL을 통해 로그인 하지 않아도 접근이 가능하다.로직을 하나하나 작성해도 되지만, 효율적이지 않다.이러한 공통 관심사는 AOP로도 해결 가능하지만, 웹 관련된
모든 요청을 로그로 남기는 필터를 개발하고 적용해보자.LogFilter - 로그 필터public class LogFilter implements Filter {}필터를 사용하려면 필터 인터페이스를 구현해야 한다.doFilter(ServletRequest request,
로그인 되지 않은 사용자는 상품 관리 뿐만 아니라 미래에 개발될페이지에도 접근하지 못하도록 하자.LoginCheckFilter - 인증 체크 필터whitelist = {"/", "/members/add", "/login", "/logout","/css/\*"};인증 필

스프링 인터셉터도 서블릿 필터와 같이 웹과 관련된 공통 관심 사항을 효과적으로 해결할 수 있는 기술이다.서블릿 필터가 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 스프링 MVC가 제공하는 기술이다. 둘다 웹과 관련된 공통 관심 사항을 처리하지만, 적용되는 순서와 범
LogInterceptor - 요청 로그 인터셉터String uuid = UUID.randomUUID().toString()요청 로그를 구분하기 위한 uuid 를 생성한다.request.setAttribute(LOG_ID, uuid)서블릿 필터의 경우 지역변수로 해결이
서블릿 필터에서 사용했던 인증 체크 기능을 스프링 인터셉터로 개발해보자.LoginCheckInterceptor서블릿 필터와 비교해서 코드가 매우 간결하다. 인증이라는 것은 컨트롤러 호출 전에만 호출되면 된다. 따라서 preHandle 만 구현하면 된다.순서 주의, 세밀
ArgumentResolver 활용이번 시간에는 해당 기능을 사용해서 로그인 회원을 조금 편리하게 찾아보자.HomeController - 추가@Login 애노테이션이 있으면 직접 만든 ArgumentResolver 가 동작해서 자동으로 세션에 있는 로그인 회원을 찾아주

업로드중..
스프링이 아닌 순수 서블릿 컨테이너는 예외를 어떻게 처리하는지 알아보자.서블릿은 다음 2가지 방식으로 예외 처리를 지원한다.Exception (예외)response.sendError(HTTP 상태 코드, 오류 메시지)자바 직접 실행자바의 메인 메서드를 직접 실행하는 경
서블릿 컨테이너가 제공하는 기본 예외 처리 화면은 고객 친화적이지 않다. 서블릿이 제공하는 오류 화면 기능을 사용해보자.서블릿은 Exception (예외)가 발생해서 서블릿 밖으로 전달되거나 또는 response.sendError() 가 호출 되었을 때 각각의 상황에
블릿은 Exception (예외)가 발생해서 서블릿 밖으로 전달되거나 또는 response.sendError() 가 호출 되었을 때 설정된 오류 페이지를 찾는다.예외 발생 흐름sendError 흐름WAS는 해당 예외를 처리하는 오류 페이지 정보를 확인한다.new Err
예외 발생과 오류 페이지 요청 흐름오류가 발생하면 오류 페이지를 출력하기 위해 WAS 내부에서 다시 한번 호출이 발생한다. 이때 필터, 서블릿, 인터셉터도 모두 다시 호출된다. 그런데 로그인 인증 체크 같은 경우를 생각해보면, 이미 한번 필터나, 인터셉터에서 로그인 체
인터셉터 중복 호출 제거LogInterceptor - DispatcherType 로그 추가앞서 필터의 경우에는 필터를 등록할 때 어떤 DispatcherType 인 경우에 필터를 적용할 지 선택할 수 있었다. 그런데 인터셉터는 서블릿이 제공하는 기능이 아니라 스프링이
지금까지 예외 처리 페이지를 만들기 위해서 다음과 같은 복잡한 과정을 거쳤다.WebServerCustomizer 를 만들고예외 종류에 따라서 ErrorPage 를 추가하고예외 처리용 컨트롤러 ErrorPageController 를 만듬스프링 부트는 이런 과정을 모두 기
BasicErrorController가 제공하는 기본 정보들BasicErrorController 컨트롤러는 다음 정보를 model에 담아서 뷰에 전달한다. 뷰 템플릿은 이 값을 활용해서 출력할 수 있다.오류 정보 추가 - resources/templates/error/
오류 페이지는 단순히 고객에게 오류 화면을 보여주고끝이지만, API는 각 오류 상황에 맞는 오류 응답 스펙을 정하고, JSON으로 데이터를 내려주어야 한다.지금부터 API의 경우 어떻게 예외 처리를 하면 좋은지 알아보자.API도 오류 페이지에서 설명했던 것 처럼 처음으
API 예외 처리도 스프링 부트가 제공하는 기본 오류 방식을 사용할 수 있다.스프링 부트가 제공하는 BasicErrorController 코드를 보자.BasicErrorController 코드errorHtml() : produces = MediaType.TEXT_HTM

예외가 발생해서 서블릿을 넘어 WAS까지 예외가 전달되면 HTTP 상태코드가 500으로 처리된다.발생하는 예외에 따라서 400, 404 등등 다른 상태코드로 처리하고 싶다.오류 메시지, 형식등을 API마다 다르게 처리하고 싶다.상태코드 변환예를 들어서 IllegalAr
예외를 여기서 마무리하기예외가 발생하면 WAS까지 예외가 던져지고, WAS에서 오류 페이지 정보를 찾아서 다시 /error 를 호출하는 과정은 생각해보면 너무 복잡하다. ExceptionResolver 를 활용하면 예외가 발생했을 때 이런 복잡한 과정 없이 여기에서 문
스프링 부트가 기본으로 제공하는 ExceptionResolver 는 다음과 같다.HandlerExceptionResolverComposite 에 다음 순서로 등록1\. ExceptionHandlerExceptionResolver2\. ResponseStatusExcep
이번에는 DefaultHandlerExceptionResolver 를 살펴보자.DefaultHandlerExceptionResolver 는 스프링 내부에서 발생하는 스프링 예외를 해결한다.대표적으로 파라미터 바인딩 시점에 타입이 맞지 않으면 내부에서 TypeMismat
API의 오류는 각 시스템 마다 응답의 모양도 다르고, 스펙도 모두 다르다.지금까지 살펴본 BasicErrorController 를 사용하거나 HandlerExceptionResolver 를 직접 구현하는 방식으로 API 예외를 다루기는 쉽지 않다.@Exception
@ExceptionHandler 를 사용해서 예외를 깔끔하게 처리할 수 있게 되었지만, 정상 코드와 예외 처리 코드가 하나의 컨트롤러에 섞여 있다. @ControllerAdvice 또는 @RestControllerAdvice 를 사용하면둘을 분리할 수 있다.ExCont

업로드중..
문자를 숫자로 변환하거나, 반대로 숫자를 문자로 변환해야 하는 것 처럼 애플리케이션을 개발하다 보면 타입을 변환해야 하는 경우가 상당히 많다.다음 예를 보자.HelloController - 문자 타입을 숫자 타입으로 변경실행http://localhost:808
타입 컨버터를 어떻게 사용하는지 코드로 알아보자.주의Converter 라는 이름의 인터페이스가 많으니 조심해야 한다.org.springframework.core.convert.converter.Converter 를 사용해야 한다.먼저 가장 단순한 형태인 문자를 숫자로
이렇게 타입 컨버터를 하나하나 직접 찾아서 타입 변환에 사용하는 것은 매우 불편하다. 그래서 스프링은 개별 컨버터를 모아두고 그것들을 묶어서 편리하게 사용할 수 있는 기능을 제공하는데, 이것이 바로 컨버전 서비스( ConversionService )이다.Conversi
웹 애플리케이션에 Converter 를 적용해보자.WebConfig - 컨버터 등록스프링은 내부에서 ConversionService 를 제공한다. 우리는 WebMvcConfigurer 가 제공하는 addFormatters() 를 사용해서 추가하고 싶은 컨버터를 등록하면
이번에는 뷰 템플릿에 컨버터를 적용하는 방법을 알아보자.타임리프는 렌더링 시에 컨버터를 적용해서 렌더링 하는 방법을 편리하게 지원한다.이전까지는 문자를 객체로 변환했다면, 이번에는 그 반대로 객체를 문자로 변환하는 작업을 확인할 수 있다.ConverterControll
포맷터( Formatter )는 객체를 문자로 변경하고, 문자를 객체로 변경하는 두 가지 기능을 모두 수행한다.String print(T object, Locale locale) : 객체를 문자로 변경한다.T parse(String text, Locale locale)
컨버전 서비스에는 컨버터만 등록할 수 있고, 포맷터를 등록할 수 는 없다. 그런데 생각해보면 포맷터는 객체 -> 문자, 문자 -> 객체로 변환하는 특별한 컨버터일 뿐이다.포맷터를 지원하는 컨버전 서비스를 사용하면 컨버전 서비스에 포맷터를 추가할 수 있다. 내부에서 어댑
포맷터를 웹 애플리케이션에 적용해보자.WebConfig - 수정주의 StringToIntegerConverter , IntegerToStringConverter 를 꼭 주석처리 하자.MyNumberFormatter 도 숫자 -> 문자, 문자 -> 숫자로 변경하기 때문에
스프링은 자바에서 기본으로 제공하는 타입들에 대해 수 많은 포맷터를 기본으로 제공한다.IDE에서 Formatter 인터페이스의 구현 클래스를 찾아보면 수 많은 날짜나 시간 관련 포맷터가 제공되는것을 확인할 수 있다.그런데 포맷터는 기본 형식이 지정되어 있기 때문에, 객

일반적으로 사용하는 HTML Form을 통한 파일 업로드를 이해하려면 먼저 폼을 전송하는 다음 두 가지 방식의 차이를 이해해야 한다.HTML 폼 전송 방식application/x-www-form-urlencodedmultipart/form-dataapplication/

편의상 index.html 을 추가해두자.resources/static/index.html
먼저 서블릿을 통한 파일 업로드를 코드와 함께 알아보자.ServletUploadControllerV1request.getParts() : multipart/form-data 전송 방식에서 각각 나누어진 부분을 받아서 확인할 수 있다.resources/templates/
서블릿이 제공하는 Part 에 대해 알아보고 실제 파일도 서버에 업로드 해보자.먼저 파일을 업로드를 하려면 실제 파일이 저장되는 경로가 필요하다.해당 경로에 실제 폴더를 만들어두자.그리고 다음에 만들어진 경로를 입력해두자.application.propertiesServ
스프링은 MultipartFile 이라는 인터페이스로 멀티파트 파일을 매우 편리하게 지원한다.SpringUploadController코드를 보면 스프링 답게 딱 필요한 부분의 코드만 작성하면 된다.@RequestParam MultipartFile file업로드하는 HT
실제 파일이나 이미지를 업로드, 다운로드 할 때는 몇가지 고려할 점이 있는데, 구체적인 예제로 알아보자.요구사항상품을 관리상품 이름첨부파일 하나이미지 파일 여러개첨부파일을 업로드 다운로드 할 수 있다.업로드한 이미지를 웹 브라우저에서 확인할 수 있다.Item - 상품

테스트에서도 lombok을 사용하기 위해 다음 코드를 추가하자.build.gradle이 설정을 추가해야 테스트 코드에서 @Slfj4 같은 롬복 애노테이션을 사용할 수 있다.
H2 데이터베이스는 개발이나 테스트 용도로 사용하기 좋은 가볍고 편리한 DB이다. 그리고 SQL을 실행할 수 있는 웹 화면을 제공한다.설치https://www.h2database.com/html/main.htmlh2 데이터베이스 버전은 스프링 부트 버전에 맞춘

애플리케이션을 개발할 때 중요한 데이터는 대부분 데이터베이스에 보관한다.클라이언트가 애플리케이션 서버를 통해 데이터를 저장하거나 조회하면, 애플리케이션 서버는 다음 과정을 통해서 데이터베이스를 사용한다.1\. 커넥션 연결: 주로 TCP/IP를 사용해서 커넥션을 연결한다

DBC는 1997년에 출시될 정도로 오래된 기술이고, 사용하는 방법도 복잡하다. 그래서 최근에는 JDBC를 직접 사용하기 보다는 JDBC를 편리하게 사용하는 다양한 기술이 존재한다. 대표적으로 SQL Mapper와 ORM 기술로 나눌 수 있다.SQL Mapper장점:

애플리케이션과 데이터베이스를 연결해보자.ConnectionConst이터베이스에 접속하는데 필요한 기본 정보를 편리하게 사용할 수 있도록 상수로 만들었다.이제 JDBC를 사용해서 실제 데이터베이스에 연결하는 코드를 작성해보자.DBConnectionUtil데이터베이스에 연
이제 본격적으로 JDBC를 사용해서 애플리케이션을 개발해보자.여기서는 JDBC를 사용해서 회원( Member ) 데이터를 데이터베이스에 관리하는 기능을 개발해보자.Member회원의 ID와 해당 회원이 소지한 금액을 표현하는 단순한 클래스이다. 앞서 만들어둔 member

이번에는 JDBC를 통해 이전에 저장한 데이터를 조회하는 기능을 개발해보자.MemberRepositoryV0 - 회원 조회 추가findById() - 쿼리 실행sql : 데이터 조회를 위한 select SQL을 준비한다.rs = pstmt.executeQuery() 데
수정과 삭제는 등록과 비슷하다. 등록, 수정, 삭제처럼 데이터를 변경하는 쿼리는 executeUpdate() 를 사용하면 된다.MemberRepositoryV0 - 회원 수정 추가executeUpdate() 는 쿼리를 실행하고 영향받은 row수를 반환한다. 여기서는 하

데이터베이스 커넥션을 획득할 때는 다음과 같은 복잡한 과정을 거친다.1\. 애플리케이션 로직은 DB 드라이버를 통해 커넥션을 조회한다.2\. DB 드라이버는 DB와 TCP/IP 커넥션을 연결한다. 물론 이 과정에서 3 way handshake 같은 TCP/IP 연결을

커넥션을 얻는 방법은 앞서 학습한 JDBC DriverManager 를 직접 사용하거나, 커넥션 풀을 사용하는 등 다양한 방법이 존재한다.우리가 앞서 JDBC로 개발한 애플리케이션 처럼 DriverManager 를 통해서 커넥션을 획득하다가, 커넥션 풀을 사용하는 방법
예제를 통해 DataSource 를 알아보자.먼저 기존에 개발했던 DriverManager 를 통해서 커넥션을 획득하는 방법을 확인해보자.ConnectionTest - 드라이버 매니저실행 결과이번에는 스프링이 제공하는 DataSource 가 적용된 DriverManag
이번에는 DataSource 를 통해 커넥션 풀을 사용하는 예제를 알아보자.ConnectionTest - 데이터소스 커넥션 풀 추가HikariCP 커넥션 풀을 사용한다. HikariDataSource 는 DataSource 인터페이스를 구현하고 있다.커넥션 풀 최대 사
이번에는 애플리케이션에 DataSource 를 적용해보자.MemberRepositoryV1DataSource 의존관계 주입외부에서 DataSource 를 주입 받아서 사용한다. 이제 직접 만든 DBConnectionUtil 을 사용하지 않아도 된다.DataSource
데이터를 저장할 때 단순히 파일에 저장해도 되는데, 데이터베이스에 저장하는 이유는 무엇일까?여러가지 이유가 있지만, 가장 대표적인 이유는 바로 데이터베이스는 트랜잭션이라는 개념을 지원하기 때문이다.트랜잭션을 이름 그대로 번역하면 거래라는 뜻이다. 이것을 쉽게 풀어서 이

트랜잭션을 더 자세히 이해하기 위해 데이터베이스 서버 연결 구조와 DB 세션에 대해 알아보자.사용자는 웹 애플리케이션 서버(WAS)나 DB 접근 툴 같은 클라이언트를 사용해서 데이터베이스 서버에 접근할 수 있다. 클라이언트는 데이터베이스 서버에 연결을 요청하고 커넥션을

트랜잭션 동작을 예제를 통해 확인해보자. 이번 시간에는 먼저 트랜잭션의 동작 개념의 전체 그림을 이해하는데 집중하자. 다음 시간에는 실제 SQL을 실행하면서 실습해보겠다.참고로 지금부터 설명하는 내용은 트랜잭션 개념의 이해를 돕기 위해 예시로 설명하는 것이다. 구체적인
이전에 설명한 예제를 돌려보기 전에 먼저 자동 커밋, 수동 커밋에 대해 알아보자.예제에 사용되는 스키마는 다음과 같다.자동 커밋트랜잭션을 사용하려면 먼저 자동 커밋과 수동 커밋을 이해해야 한다.자동 커밋으로 설정하면 각각의 쿼리 실행 직후에 자동으로 커밋을 호출한다.

지금까지 설명한 예시를 직접 확인해보자.먼저 H2 데이터베이스 웹 콘솔 창을 2개 열어두자.주의!H2 데이터베이스 웹 콘솔 창을 2개 열때 기존 URL을 복사하면 안된다. 꼭 http://localhost:8082 를 직접 입력해서 완전히 새로운 세션에서 연결

이번에는 계좌이체 예제를 통해 트랜잭션이 어떻게 사용되는지 조금 더 자세히 알아보자. 다음 3가지 상황을 준비했다.계좌이체 정상계좌이체 문제 상황 - 커밋계좌이체 문제 상황 - 롤백계좌이체 정상계좌이체가 발생하는 정상 흐름을 알아보자.기본 데이터 입력먼저 다음 SQL로

세션1이 트랜잭션을 시작하고 데이터를 수정하는 동안 아직 커밋을 수행하지 않았는데, 세션2에서 동시에 같은 데이터를 수정하게 되면 여러가지 문제가 발생한다. 바로 트랜잭션의 원자성이 깨지는 것이다. 여기에 더해서 세션1이 중간에 롤백을 하게 되면 세션2는 잘못된 데이터

앞서 배운 내용을 실습해보자.실습을 위해 데이터를 기본 데이터를 입력하자.기본 데이터 입력 - SQL세션1세션1이 트랜잭션을 시작하고, memberA 의 데이터를 500원으로 업데이트 했다. 아직 커밋은 하지 않았다.memberA 로우의 락은 세션1이 가지게 된다.세션

일반적인 조회는 락을 사용하지 않는다데이터베이스마다 다르지만, 보통 데이터를 조회할 때는 락을 획득하지 않고 바로 데이터를 조회할 수 있다.예를 들어서 세션1이 락을 획득하고 데이터를 변경하고 있어도, 세션2에서 데이터를 조회는 할 수 있다. 물론 세션2에서 조회가 아
실제 애플리케이션에서 DB 트랜잭션을 사용해서 계좌이체 같이 원자성이 중요한 비즈니스 로직을 어떻게 구현하는지 알아보자.먼저 트랜잭션 없이 단순하게 계좌이체 비즈니스 로직만 구현해보자.MemberServiceV1formId 의 회원을 조회해서 toId 의 회원에게 mo

이번에는 DB 트랜잭션을 사용해서 앞서 발생한 문제점을 해결해보자.애플리케이션에서 트랜잭션을 어떤 계층에 걸어야 할까? 쉽게 이야기해서 트랜잭션을 어디에서 시작하고, 어디에서 커밋해야할까?트랜잭션은 비즈니스 로직이 있는 서비스 계층에서 시작해야 한다. 비즈니스 로직이

애플리케이션 구조여러가지 애플리케이션 구조가 있지만, 가장 단순하면서 많이 사용하는 방법은 역할에 따라 3가지 계층으로 나누는 것이다.프레젠테이션 계층UI와 관련된 처리 담당웹 요청과 응답사용자 요청을 검증주 사용 기술: 서블릿과 HTTP 같은 웹 기술, 스프링 MVC
현재 서비스 계층은 트랜잭션을 사용하기 위해서 JDBC 기술에 의존하고 있다. 향후 JDBC에서 JPA 같은 다른 데이터 접근 기술로 변경하면, 서비스 계층의 트랜잭션 관련 코드도 모두 함께 수정해야 한다.구현 기술에 따른 트랜잭션 사용법트랜잭션은 원자적 단위의 비즈니

스프링이 제공하는 트랜잭션 매니저는 크게 2가지 역할을 한다.트랜잭션 추상화리소스 동기화리소스 동기화트랜잭션을 유지하려면 트랜잭션의 시작부터 끝까지 같은 데이터베이스 커넥션을 유지해아한다. 결국 같은 커넥션을 동기화(맞추어 사용)하기 위해서 이전에는 파라미터로 커넥션을
이제 본격적으로 애플리케이션 코드에 트랜잭션 매니저를 적용해보자.MemberRepositoryV3커넥션을 파라미터로 전달하는 부분이 모두 제거되었다.DataSourceUtils.getConnection()getConnection() 에서 DataSourceUtils.g

그림으로 트랜잭션 매니저의 전체 동작 흐름을 자세히 이해해보자.클라이언트의 요청으로 서비스 로직을 실행한다.1\. 서비스 계층에서 transactionManager.getTransaction() 을 호출해서 트랜잭션을 시작한다.2\. 트랜잭션을 시작하려면 먼저 데이터베
트랜잭션을 사용하는 로직을 살펴보면 다음과 같은 패턴이 반복되는 것을 확인할 수 있다.트랜잭션 사용 코드트랜잭션을 시작하고, 비즈니스 로직을 실행하고, 성공하면 커밋하고, 예외가 발생해서 실패하면 롤백한다.다른 서비스에서 트랜잭션을 시작하려면 try , catch ,

지금까지 트랜잭션을 편리하게 처리하기 위해서 트랜잭션 추상화도 도입하고, 추가로 반복적인 트랜잭션 로직을 해결하기 위해 트랜잭션 템플릿도 도입했다.트랜잭션 템플릿 덕분에 트랜잭션을 처리하는 반복 코드는 해결할 수 있었다. 하지만 서비스 계층에 순수한 비즈니스 로직만 남
트랜잭션 AOP를 사용하는 새로운 서비스 클래스를 만들자.MemberServiceV3_3순수한 비즈니스 로직만 남기고, 트랜잭션 관련 코드는 모두 제거했다.스프링이 제공하는 트랜잭션 AOP를 적용하기 위해 @Transactional 애노테이션을 추가했다.@Transac

트랜잭션 AOP가 사용된 전체 흐름을 그림으로 정리해보자.선언적 트랜잭션 관리 vs 프로그래밍 방식 트랜잭션 관리선언적 트랜잭션 관리(Declarative Transaction Management)@ - Transactional 애노테이션 하나만 선언해서 매우 편리하
스프링 부트가 등장하기 이전에는 데이터소스와 트랜잭션 매니저를 개발자가 직접 스프링 빈으로 등록해서 사용했다. 그런데 스프링 부트로 개발을 시작한 개발자라면 데이터소스나 트랜잭션 매니저를 직접 등록한 적이 없을 것이다.데이터소스와 트랜잭션 매니저를 스프링 빈으로 직접

스프링이 제공하는 예외 추상화를 이해하기 위해서는 먼저 자바 기본 예외에 대한 이해가 필요하다.Object : 예외도 객체이다. 모든 객체의 최상위 부모는 Object 이므로 예외의 최상위 부모도 Object 이다.Throwable : 최상위 예외이다. 하위에 Exce

예외는 폭탄 돌리기와 같다. 잡아서 처리하거나, 처리할 수 없으면 밖으로 던져야한다.예외에 대해서는 2가지 기본 규칙을 기억하자.1\. 예외는 잡아서 처리하거나 던져야 한다.2\. 예외를 잡거나 던질 때 지정한 예외뿐만 아니라 그 예외의 자식들도 함께 처리된다.예를 들
Exception 과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다. 단 RuntimeException 은 예외로 한다.체크 예외는 잡아서 처리하거나, 또는 밖으로 던지도록 선언해야한다. 그렇지 않으면 컴파일 오류가 발생한다.체크 예외 전체 코드Exceptio
RuntimeException 과 그 하위 예외는 언체크 예외로 분류된다.언체크 예외는 말 그대로 컴파일러가 예외를 체크하지 않는다는 뜻이다.언체크 예외는 체크 예외와 기본적으로 동일하다. 차이가 있다면 예외를 던지는 throws 를 선언하지 않고, 생략할 수 있다.

이번에는 런타임 예외를 사용해보자.SQLException 을 런타임 예외인 RuntimeSQLException 으로 변환했다.ConnectException 대신에 RuntimeConnectException 을 사용하도록 바꾸었다.런타임 예외이기 때문에 서비스, 컨트롤러
예외를 전환할 때는 꼭! 기존 예외를 포함해야 한다. 그렇지 않으면 스택 트레이스를 확인할 때 심각한 문제가 발생한다.로그를 출력할 때 마지막 파라미터에 예외를 넣어주면 로그에 스택 트레이스를 출력할 수 있다.예) log.info("message={}", "messag

서비스 계층은 가급적 특정 구현 기술에 의존하지 않고, 순수하게 유지하는 것이 좋다. 이렇게 하려면 예외에 대한 의존도 함께 해결해야한다.예를 들어서 서비스가 처리할 수 없는 SQLException 에 대한 의존을 제거하려면 어떻게 해야할까?서비스가 처리할 수 없으므로
실제 코드에 런타임 예외를 사용하도록 적용해보자.MemberRepository 인터페이스MyDbException 런타임 예외RuntimeException 을 상속받았다. 따라서 MyDbException 은 런타임(언체크) 예외가 된다.MemberRepositoryV4_

데이터베이스 오류에 따라서 특정 예외는 복구하고 싶을 수 있다.예를 들어서 회원 가입시 DB에 이미 같은 ID가 있으면 ID 뒤에 숫자를 붙여서 새로운 ID를 만들어야 한다고 가정해보자.ID를 hello 라고 가입 시도 했는데, 이미 같은 아이디가 있으면 hello12

스프링은 앞서 설명한 문제들을 해결하기 위해 데이터 접근과 관련된 예외를 추상화해서 제공한다.스프링은 데이터 접근 계층에 대한 수십 가지 예외를 정리해서 일관된 예외 계층을 제공한다.각각의 예외는 특정 기술에 종속적이지 않게 설계되어 있다. 따라서 서비스 계층에서도 스
이제 우리가 만든 애플리케이션에 스프링이 제공하는 데이터 접근 예외 추상화와 SQL 예외 변환기를 적용해보자.MemberRepositoryV4_2기존 코드에서 스프링 예외 변환기를 사용하도록 변경되었다.MemberServiceV4Test - 수정MemberReposit
지금까지 서비스 계층의 순수함을 유지하기 위해 수 많은 노력을 했고, 덕분에 서비스 계층의 순수함을 유지하게 되었다. 이번에는 리포지토리에서 JDBC를 사용하기 때문에 발생하는 반복 문제를 해결해보자.JDBC 반복 문제커넥션 조회, 커넥션 동기화PreparedState
적용 데이터 접근 기술JdbcTemplateMyBatisJPA, Hibernate스프링 데이터 JPAQuerydsl여기에는 크게 2가지 분류가 있다.SQLMapperJdbcTemplateMyBatisORM 관련 기술JPA, Hibernate스프링 데이터 JPAQuery

스프링 MVC 1편에서 마지막에 완성한 상품 관리 프로젝트를 떠올려보자.이 프로젝트는 단순히 메모리에 상품 데이터를 저장하도록 되어 있었다.여기에 메모리가 아닌 실제 데이터 접근 기술들을 하나씩 적용해가면서, 각각의 데이터 접근 기술들을 어떻게 사용하는지, 장단점은 무
프로젝트 설정build.gradlespring-boot-starter-thymeleaf : 타임리프 사용spring-boot-starter-web : 스프링 웹, MVC 기능 사용spring-boot-starter-test : 스프링이 제공하는 테스트 기능lombok
스프링 부트 설정 분석MemoryConfigItemServiceV1 , MemoryItemRepository 를 스프링 빈으로 등록하고 생성자를 통해 의존관계를 주입한다.참고로 여기서는 서비스와 리포지토리는 구현체를 편리하게 변경하기 위해, 이렇게 수동으로 빈을 등록했
ItemRepositoryTestafterEach : 테스트는 서로 영향을 주면 안된다. 따라서 각각의 테스트가 끝나고 나면 저장한 데이터를 제거해야 한다. @AfterEach 는 각각의 테스트의 실행이 끝나는 시점에 호출된다. 여기서는 메모리 저장소를 완전히 삭제해서
이제부터 다양한 데이터 접근 기술들을 활용해서 메모리가 아닌 데이터베이스에 데이터를 보관하는 방법을 알아보자.먼저 H2 데이터베이스에 접근해서 item 테이블을 생성하자.generated by default as identityidentity 전략이고 하는데, 기본 키
SQL을 직접 사용하는 경우에 스프링이 제공하는 JdbcTemplate은 아주 좋은 선택지다. JdbcTemplate은 JDBC를 매우 편리하게 사용할 수 있게 도와준다.장점설정의 편리함JdbcTemplate은 spring-jdbc 라이브러리에 포함되어 있는데, 이 라
이제부터 본격적으로 JdbcTemplate을 사용해서 메모리에 저장하던 데이터를 데이터베이스에 저장해보자.ItemRepository 인터페이스가 있으니 이 인터페이스를 기반으로 JdbcTemplate을 사용하는 새로운 구현체를 개발하자.JdbcTemplateItemRe
결과를 검색하는 findAll() 에서 어려운 부분은 사용자가 검색하는 값에 따라서 실행하는 SQL이 동적으로 달려져야 한다는 점이다.예를 들어서 다음과 같다.검색 조건이 없음상품명( itemName )으로 검색최대 가격( maxPrice )으로 검색상품명( itemN
실제 코드가 동작하도록 구성하고 실행해보자.JdbcTemplateV1ConfigItemRepository 구현체로 JdbcTemplateItemRepositoryV1 이 사용되도록 했다. 이제 메모리 저장소가 아니라 실제 DB에 연결하는 JdbcTemplate이 사용된
JdbcTemplate을 기본으로 사용하면 파라미터를 순서대로 바인딩 한다.따라서 순서만 잘지키면 문제없을거 같지만 누군가 SQL코드의 순서를 변경했다고 가정해보자.파라미터값이 바뀌는 매우 심각한 문제가 발생할것이다. 이럴일이 없을 것 같지만, 실무에서는 파라미터가 1
이름 지정 파라미터파라미터를 전달하려면 Map 처럼 key , value 데이터 구조를 만들어서 전달해야 한다. 여기서 key 는 :파리이터이름 으로 지정한, 파라미터의 이름이고 , value 는 해당 파라미터의 값이 된다.다음 코드를 보면 이렇게 만든 파라미터( pa
이제 이름 지정 파라미터를 사용하도록 구성하고 실행해보자.JdbcTemplateV2Config앞서 개발한 JdbcTemplateItemRepositoryV2 를 사용하도록 스프링 빈에 등록한다.ItemServiceApplication - 변경JdbcTemplateV2C
JdbcTemplate은 INSERT SQL를 직접 작성하지 않아도 되도록 SimpleJdbcInsert 라는 편리한 기능을 제공한다.JdbcTemplateItemRepositoryV3기본JdbcTemplateItemRepositoryV3 은 ItemRepository
주요 기능JdbcTemplate이 제공하는 주요 기능은 다음과 같다.JdbcTemplate순서 기반 파라미터 바인딩을 지원한다.NamedParameterJdbcTemplate이름 기반 파라미터 바인딩을 지원한다. (권장)SimpleJdbcInsertINSERT SQL을
데이터 접근 기술에 대해서 더 알아보기 전에 데이터베이스에 연동하는 테스트에 대해서 알아보자. 데이터 접근 기술은 실제 데이터베이스에 접근해서 데이터를 잘 저장하고 조회할 수 있는지 확인하는 것이 필요하다.지금부터 테스트를 실행할 때 실제 데이터베이스를 연동해서 진행해
로컬에서 사용하는 애플리케이션 서버와 테스트에서 같은 데이터베이스를 사용하고 있으니 테스트에서 문제가 발생한다.이런 문제를 해결하려면 테스트를 다른 환경과 철저하게 분리해야 한다.가장 간단한 방법은 테스트 전용 데이터베이스를 별도로 운영하는 것이다.H2 데이터베이스를
트랜잭션과 롤백 전략테스트가 끝나고 나서 트랜잭션을 강제로 롤백해버리면 데이터가 깔끔하게 제거된다.테스트를 하면서 데이터를 이미 저장했는데, 중간에 테스트가 실패해서 롤백을 호출하지 못해도 괜찮다.트랜잭션을 커밋하지 않았기 때문에 데이터베이스에 해당 데이터가 반영되지

스프링은 테스트 데이터 초기화를 위해 트랜잭션을 적용하고 롤백하는 방식을 @Transactional 애노테이션 하나로 깔끔하게 해결해준다.이전에 테스트에 트랜잭션과 롤백을 위해 추가했던 코드들을 주석 처리하자.temRepositoryTest 테스트 코드에 스프링이 제공
테스트 케이스를 실행하기 위해서 별도의 데이터베이스를 설치하고, 운영하는 것은 상당히 번잡한 작업이다. 단순히 테스트를 검증할 용도로만 사용하기 때문에 테스트가 끝나면 데이터베이스의 데이터를 모두 삭제해도 된다. 더 나아가서 테스트가 끝나면 데이터베이스 자체를 제거해도
스프링 부트는 개발자에게 정말 많은 편리함을 제공하는데, 임베디드 데이터베이스에 대한 설정도 기본으로 제공한다.스프링 부트는 데이터베이스에 대한 별다른 설정이 없으면 임베디드 데이터베이스를 사용한다.앞서 직접 설정했던 메모리 DB용 데이터소스를 주석처리하자.그리고 테스
MyBatis는 앞서 설명한 JdbcTemplate보다 더 많은 기능을 제공하는 SQL Mapper 이다. 기본적으로 JdbcTemplate이 제공하는 대부분의 기능을 제공한다. JdbcTemplate과 비교해서 MyBatis의 가장 매력적인 점은 SQL을 XML에 편
mybatis-spring-boot-starter 라이브러리를 사용하면 MyBatis를 스프링과 통합하고, 설정도 아주 간단히 할 수 있다.mybatis-spring-boot-starter 라이브러리를 사용해서 간단히 설정하는 방법을 알아보자.build.gradle 에
이제부터 본격적으로 MyBatis를 사용해서 데이터베이스에 데이터를 저장해보자. XML에 작성한다는 점을 제외하고는 JDBC 반복을 줄여준다는 점에서 기존 JdbcTemplate과 거의 유사하다.ItemMapper마이바티스 매핑 XML을 호출해주는 매퍼 인터페이스이다.
MyBatisItemRepositoryItemRepository 를 구현해서 MyBatisItemRepository 를 만들자.MyBatisItemRepository 는 단순히 ItemMapper 에 기능을 위임한다.MyBatisConfigMyBatisConfig 는

생각해보면 지금까지 진행한 내용중에 약간 이상한 부분이 있다.ItemMapper 매퍼 인터페이스의 구현체가 없는데 어떻게 동작한 것일까?ItemMapper 인터페이스 부분은 MyBatis 스프링 연동 모듈에서 자동으로 처리해주는데 다음과 같다. 1\. 애플리케이션 로딩
동적 SQL마이바티스가 제공하는 최고의 기능이자 마이바티스를 사용하는 이유는 바로 동적 SQL 기능 때문이다.동적 쿼리를 위해 제공되는 기능은 다음과 같다.ifchoose (when, otherwise)trim (where, set)foreach공식 메뉴얼에서 제공하는
애노테이션으로 SQL 작성다음과 같이 XML 대신에 애노테이션에 SQL을 작성할 수 있다.@Insert , @Update , @Delete , @Select 기능이 제공된다.이 경우 XML에는 <select id="findById"> ~ </select> 는
스프링과 JPA는 자바 엔터프라이즈(기업) 시장의 주력 기술이다.스프링이 DI 컨테이너를 포함한 애플리케이션 전반의 다양한 기능을 제공한다면, JPA는 ORM 데이터 접근 기술을 제공한다.스프링 + 데이터 접근기술의 조합을 구글 트랜드로 비교했을 때글로벌에서는 스프링+

현재 대부분 현대적인 애플리케이션은 객체 지향 언어를 사용한다.또한 데이터를 저장하기 위해서는 관계형 DB를 사용한다.결론적으로 지금 시대는 객체를 관계형 DB에 관리한다.문제점이 있다.애플리케이션은 객체지향적으로 설계하는데 DB는 SQL만 알아듣기 때문에 SQL을 계

Java Persistence API자바 진영의 ORM 기술 표준Object-relational mapping(객체 관계 매핑)객체는 객체대로 설계관계형 데이터베이스는 관계형 데이터베이스대로 설계ORM 프레임워크가 중간에서 매핑대중적인 언어에는 대부분 ORM 기술이 존
컨트롤러에서 검증 로직이 차지하는 부분은 매우 크다. 이런 경우 별도의 클래스로 역할을 분리하는 것이 좋다. 그리고 이렇게 분리한 검증 로직을 재사용 할 수도 있다. ItemValidator 를 만들자. 스프링은 검증을 체계적으로 제공하기 위해 다음 인터페이스를 제
공식 사이트: https://www.thymeleaf.org/ 공식 메뉴얼 - 기본 기능: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 공식 메뉴얼 - 스프링 통합: https://www.thymel
MVC 패턴을 적용한 덕분에 컨트롤러의 역할과 뷰를 렌더링 하는 역할을 명확하게 구분할 수 있다. 특히 뷰는 화면을 그리는 역할에 충실한 덕분에, 코드가 깔끔하고 직관적이다. 단순하게 모델에서 필요한 데이터를 꺼내고, 화면을 만들면 된다. 그런데 컨트롤러는 딱 봐도 중
spring-boot-starter-data-jpa 라이브러리를 사용하면 JPA와 스프링 데이터 JPA를 스프링 부트와 통합하고, 설정도 아주 간단히 할 수 있다. spring-boot-starter-data-jpa 라이브러리를 사용해서 간단히 설정하는 방법을 알아보자
JPA에서 가장 중요한 부분은 객체와 테이블을 매핑하는 것이다. JPA가 제공하는 애노테이션을 사용해서 Item 객체와 테이블을 매핑해보자.Item - ORM 매핑@Entity : JPA가 사용하는 객체라는 뜻이다. 이 에노테이션이 있어야 JPA가 인식할 수 있다. 이
JpaItemRepositoryV1 코드를 분석해보자.save() - 저장em.persist(item) : JPA에서 객체를 테이블에 저장할 때는 엔티티 매니저가 제공하는 persist() 메서드를 사용하면 된다.JPA가 만들어서 실행한 SQLJPA가 만들어서 실행한

JPA의 경우 예외가 발생하면 JPA 예외가 발생하게 된다.EntityManager 는 순수한 JPA 기술이고, 스프링과는 관계가 없다. 따라서 엔티티 매니저는 예외가 발생하면 JPA 관련 예외를 발생시킨다.JPA는 PersistenceException 과 그 하위 예

옛날 옛적에 EJB(Enterprise Java Beans)라는 기술이있었다. 지금으로 따지면 스프링, ORM 같은 기능들이 있었다.이론은 좋았지만 기술이 너무 복잡했다.EJB 지옥에서 두 명의 영웅이 나타났다.스프링스프링은 EJB 컨테이너를 대체하였다. 로드존슨이 쓴

스프링 데이터 JPA가 등장하였다.이 기술을 사용하면 아래 코드처럼 코드가 간단해진다.인터페이스만 상속받으면 기능들을 다 제공해준다.Spring Data JPA 는 인터페이스에 메서드만 적어두면 메서드 이름을 분석해서 JPQL을 만들고 실행해준다.요즘 백엔드 주요 프레

스프링 데이터 JPA는 JPA를 편리하게 사용할 수 있도록 도와주는 라이브러리이다. 수많은 편리한 기능을 제공하지만 가장 대표적인 기능은 다음과 같다.공통 인터페이스 기능쿼리 메서드 기능공통 인터페이스 기능JpaRepository 인터페이스를 통해서 기본적인 CRUD
설정스프링 데이터 JPA는 spring-boot-starter-data-jpa 라이브러리를 넣어주면 된다.build.gradle 추가그런데 이미 앞에서 JPA를 설정하면서 spring-boot-starter-data-jpa 라이브러리를 넣어주었다.여기에는 JPA , 하

JpaItemRepositoryV2의존관계와 구조ItemService 는 ItemRepository 에 의존하기 때문에 ItemService 에서 SpringDataJpaItemRepository 를 그대로 사용할 수 없다.물론 ItemService 가 SpringDa

만약 긴급 요구사항이 추가된다면?위와같이 쿼리를 짤것이다.하지만 여기에는 문제점이 있다.QUERY의 문제점QUERY는 문자, Type-check 불가능실행하기 전까지 작동여부 확인 불가만약 SQL이 클래스처럼 타입이 있고 자바 코드로 작성 할 수 있다면?이러한것을 Ty

QueryDSL쿼리 + 도메인 + 특화 + 언어쿼리에 특화된 프로그래밍 언어단순, 간결, 유창다양한 저장소 쿼리 기능 통합JPA, MongoDB, SQL 같은 기술들을 위해 type-safe SQL을 만드는 프레임워크Querydsl-JPAQuerydsl은 JPA쿼리(J
build.gradle검증 - Q 타입 생성 확인 방법옵션 선택1 - Gradle - Q타입 생성 확인 방법Gradle IntelliJ 사용법Gradle -> Tasks -> build -> cleanGradle -> Tasks -> other -> compileJav
JpaItemRepositoryV3공통Querydsl을 사용하려면 JPAQueryFactory 가 필요하다. JPAQueryFactory 는 JPA 쿼리인 JPQL을 만들기 때문에 EntityManager 가 필요하다.설정 방식은 JdbcTemplate 을 설정하는 것

스프링 데이터 JPA 예제를 다시 한번 돌아보자.중간에서 JpaItemRepositoryV2 가 어댑터 역할을 해준 덕분에 ItemService 가 사용하는 ItemRepository 인터페이스를 그대로 유지할 수 있고 클라이언트인 ItemService 의 코드를 변경

마지막에 Querydsl을 사용한 리포지토리는 스프링 데이터 JPA를 사용하지 않는 아쉬움이 있었다. 물론 Querydsl을 사용하는 리포지토리가 스프링 데이터 JPA 리포지토리를 사용하도록 해도 된다.이번에는 스프링 데이터 JPA의 기능은 최대한 살리면서, Query

먼저 본격적인 기능 설명에 앞서 지금까지 학습한 스프링 트랜잭션을 간략히 복습하면서 정리해보자.스프링 트랜잭션 추상화각각의 데이터 접근 기술들은 트랜잭션을 처리하는 방식에 차이가 있다. 예를 들어 JDBC 기술과 JPA 기술은 트랜잭션을 사용하는 코드 자체가 다르다.J

build.gradle

트랜잭션 적용 확인@Transactional 을 통해 선언적 트랜잭션 방식을 사용하면 단순히 애노테이션 하나로 트랜잭션을 적용할 수 있다. 그런데 이 기능은 트랜잭션 관련 코드가 눈에 보이지 않고, AOP를 기반으로 동작하기 때문에, 실제 트랜잭션이 적용되고 있는지 아
이번시간에는 코드를 통해 @Transactional 의 적용 위치에 따른 우선순위를 확인해보자.스프링에서 우선순위는 항상 더 구체적이고 자세한 것이 높은 우선순위를 가진다. 이것만 기억하면 스프링에서 발생하는 대부분의 우선순위를 쉽게 기억할 수 있다. 그리고 더 구체적

@Transactional 을 사용하면 스프링의 트랜잭션 AOP가 적용된다.트랜잭션 AOP는 기본적으로 프록시 방식의 AOP를 사용한다.앞서 배운 것 처럼 @Transactional 을 적용하면 프록시 객체가 요청을 먼저 받아서 트랜잭션을 처리하고, 실제 객체를 호출해

메서드 내부 호출 때문에 트랜잭션 프록시가 적용되지 않는 문제를 해결하기 위해 internal() 메서드를 별도의 클래스로 분리하자.InternalCallV2TestInternalService 클래스를 만들고 internal() 메서드를 여기로 옮겼다.이렇게 메서드 내
스프링 초기화 시점에는 트랜잭션 AOP가 적용되지 않을 수 있다.테스트를 실행해보자.초기화 코드(예: @PostConstruct )와 @Transactional 을 함께 사용하면 트랜잭션이 적용되지 않는다.왜냐하면 초기화 코드가 먼저 호출되고, 그 다음에 트랜잭션 AO
스프링 트랜잭션은 다양한 옵션을 제공한다. @Transactionalvalue, transactionManager트랜잭션을 사용하려면 먼저 스프링 빈에 등록된 어떤 트랜잭션 매니저를 사용할지 알아야 한다. 생각해보면 코드로 직접 트랜잭션을 사용할 때 분명 트랜잭션 매니

예외가 발생했는데, 내부에서 예외를 처리하지 못하고, 트랜잭션 범위( @Transactional가 적용된 AOP ) 밖으로 예외를 던지면 어떻게 될까?예외 발생시 스프링 트랜잭션 AOP는 예외의 종류에 따라 트랜잭션을 커밋하거나 롤백한다.언체크 예외인 RuntimeEx
스프링은 왜 체크 예외는 커밋하고, 언체크(런타임) 예외는 롤백할까?스프링 기본적으로 체크 예외는 비즈니스 의미가 있을 때 사용하고, 런타임(언체크) 예외는 복구 불가능한예외로 가정한다.체크 예외: 비즈니스 의미가 있을 때 사용언체크 예외: 복구 불가능한 예외참고로 꼭
트랜잭션이 둘 이상 있을 때 어떻게 동작하는지 자세히 알아보고, 스프링이 제공하는 트랜잭션 전파 (propagation)라는 개념도 알아보자.간단한 예제 코드로 스프링 트랜잭션을 실행해보자.BasicTxTest@TestConfiguration : 해당 테스트에서 필요한

이번에는 트랜잭션이 각각 따로 사용되는 경우를 확인해보자.이 예제는 트랜잭션1이 완전히 끝나고나서 트랜잭션2를 수행한다.double_commit() - BasicTxTest 추가double_commit() - 실행 로그트랜잭션1Acquired Connection Hik

트랜잭션을 각각 사용하는 것이 아니라, 트랜잭션이 이미 진행중인데, 여기에 추가로 트랜잭션을 수행하면 어떻게 될까?기존 트랜잭션과 별도의 트랜잭션을 진행해야 할까? 아니면 기존 트랜잭션을 그대로 이어 받아서 트랜잭션을 수행해야 할까?이런 경우 어떻게 동작할지 결정하는

예제 코드를 통해 스프링 트랜잭션 전파를 알아보자.inner_commit() - BasicTxTest 추가외부 트랜잭션이 수행중인데, 내부 트랜잭션을 추가로 수행했다.외부 트랜잭션은 처음 수행된 트랜잭션이다. 이 경우 신규 트랜잭션 (isNewTransaction=tr

이번에는 내부 트랜잭션은 커밋되는데, 외부 트랜잭션이 롤백되는 상황을 알아보자.논리 트랜잭션이 하나라도 롤백되면 전체 물리 트랜잭션은 롤백된다.따라서 이 경우 내부 트랜잭션이 커밋했어도, 내부 트랜잭션 안에서 저장한 데이터도 모두 함께 롤백된다.outer_rollbac

이번에는 내부 트랜잭션은 롤백되는데, 외부 트랜잭션이 커밋되는 상황을 알아보자.외부 트랜잭션만 물리 트랜잭션에 영향을 주기 때문에 물리 트랜잭션이 커밋될 것같다. 전체를 롤백해야 하는데, 스프링은 이 문제를 어떻게 해결할까?inner_rollback() - BasicT

이번에는 외부 트랜잭션과 내부 트랜잭션을 완전히 분리해서 사용하는 방법에 대해서 알아보자.이렇게 물리 트랜잭션을 분리하려면 내부 트랜잭션을 시작할 때 REQUIRES_NEW 옵션을 사용하면 된다.외부 트랜잭션과 내부 트랜잭션이 각각 별도의 물리 트랜잭션을 가진다.별도의
스프링은 다양한 트랜잭션 전파 옵션을 제공한다. 전파 옵션에 별도의 설정을 하지 않으면 REQUIRED 가 기본으로 사용된다.REQUIRED가장 많이 사용하는 기본 설정이다. 기존 트랜잭션이 없으면 생성하고, 있으면 참여한다. 트랜잭션이 필수라는 의미로 이해하면 된다.
지금까지 배운 트랜잭션 전파에 대한 내용을 실제 예제를 통해서 이해해보자.비즈니스 요구사항회원을 등록하고 조회한다.회원에 대한 변경 이력을 추적할 수 있도록 회원 데이터가 변경될 때 변경 이력을 DB LOG 테이블에 남겨야 한다.여기서는 예제를 단순화 하기 위해 회원

서비스 계층에 트랜잭션이 없을 때 - 커밋예제를 통해 서비스 계층에 트랜잭션이 없을 때 트랜잭션이 각각 어떻게 작동하는지 확인해보자.상황서비스 계층에 트랜잭션이 없다.회원, 로그 리포지토리가 각각 트랜잭션을 가지고 있다.회원, 로그 리포지토리 둘다 커밋에 성공한다.ou

트랜잭션 하나만 사용하기회원 리포지토리와 로그 리포지토리를 하나의 트랜잭션으로 묶는 가장 간단한 방법은 이 둘을 호출하는 회원 서비스에만 트랜잭션을 사용하는 것이다.singleTxMemberRepository , LogRepository 의 @Transactional

스프링은 @Transactional 이 적용되어 있으면 기본으로 REQUIRED 라는 전파 옵션을 사용한다.이 옵션은 기존 트랜잭션이 없으면 트랜잭션을 생성하고, 기존 트랜잭션이 있으면 기존 트랜잭션에 참여한다. 참여한다는 뜻은 해당 트랜잭션을 그대로 따른다는 뜻이고,

이번에는 로그 리포지토리에서 예외가 발생해서 전체 트랜잭션이 롤백되는 경우를 알아보자.outerTxOn_fail모든 곳에 트랜잭션을 적용하자.여기서는 로그예외 라고 넘겼기 때문에 LogRepository 에서 런타임 예외가 발생한다.클라이언트A가 MemberServic

비즈니스 요구사항이 변경되었다.회원 가입을 시도한 로그를 남기는데 실패하더라도 회원 가입은 유지되어야 한다.단순하게 생각해보면 LogRepository 에서 예외가 발생하면 그것을 MemberService 에서 예외를 잡아서 처리하면 될 것 같다.이렇게 하면 Membe

회원 가입을 시도한 로그를 남기는데 실패하더라도 회원 가입은 유지되어야 한다.이 요구사항을 만족하기 위해서 로그와 관련된 물리 트랜잭션을 별도로 분리해보자. 바로 REQUIRES_NEW를 사용하는 것이다.recoverException_successLogRepositor
학습을 위한 간단한 예제 프로젝트를 만들어보자.상품을 주문하는 프로세스로 가정하고, 일반적인 웹 애플리케이션에서 Controller -> Service 0> Repository로 이어지는 흐름을 최대한 단순하게 만들어보자.OrderRepositoryV0@Reposito
애플리케이션이 커지면서 점점 모니터링과 운영이 중요해지는 단계이다. 특히 최근 자주 병목이 발생하고있다. 어떤 부분에서 병목이 발생하는지, 그리고 어떤 부분에서 예외가 발생하는지를 로그를 통해 확인하는것이 점점 중요해지고 있다.기존에는 개발자가 문제가 발생한 다음에 관
애플리케이션의 모든 로직에 직접 로그를 남겨도 되지만, 그것보다는 더 효율적인 개발 방법이 필요하다.특히 트랜잭션ID와 깊이를 표현하는 방법은 기존 정보를 이어 받아야 하기 때문에 단순히 로그만 남긴다고 해결할 수 있는 것은 아니다.요구사항에 맞추어 애플리케이션에 효과