Spring Boot 2.0
- JDk 1.8이상만 지원
- Tomcat 8.5
- Flyway 5
- Hibernate 5.2
- Thymeleaf 3
알아야 할 내용
- 기본적으로 jsp는 지원하지 않는다.
- Tomcat을 설치할 필요가 없다.
- tomcat을 설치하고, Spring Boot애플리케이션을 배포할 수도 있다. 이때는 jsp가 사용가능하다.(추천하지않는다.)
- 실행가능한 jar파일이 만들어진다.(java -jar 파일명.jar) JVM만 있으면 구동된다.
- 어떤 라이브러리가 있으면 라이브러리에 대한 설정을 자동으로 해준다.
- H2 dbms에 대한 의존성을 추가하면, Spring boot는 in memory형태로 자동으로 Datasource를 설정한다.
- @SpringBootApplication 가 있으면 base package 이하로 component를 찾는다. 많을 설정을 자동으로 해주고 있다.
- Spring Boot는 application.properties 이나 application.yml 파일을 어플리케이션에서 자동으로 읽어서 매핑시켜준다.(Snake YAML 라이브러리를 내장)
- Spring Boot는 schema.sql, data.sql 파일이 있으면 자동으로 실행시켜준다.
- Spring Security가 의존성에 추가되어있으면, 자동으로 인증관련 설정이 된다.( 기본 사용자 id : user, Using generated security password: 콘솔에서 확인)
- Spring Data JPA의 JpaRepository는 인터페이스를 준비하기만 하면, 자동으로 클래스를 만들고 Bean을 생성한다.
- jar의 표준이 없다. Spring에서 실행가능한 jar를 만들기 위한 새로운 방법을 만들었다.
- 스프링 부트 플러그인(maven, gradle)을 이용하여 jar로 만들어야 한다.
- Spring boot에서는 webapp폴더아래에 파일을 작성하지 않는다.
- 정적인 리소스는 src/main/resources/static 폴더에 넣는다.
- 타임리프 관련된 템플릿은 src/main/resources/templates 폴더에 넣는다.
- 관련 라이브러리
- Spring Data JPA + QueryDSL
- Spring Security
- Gradle
- SPRING INITIALIZR(web, devtools, jpa, mysql, security, Thymeleaf(비동기))
- H2 Database
Spring boot 2.0
- @EnableWebMvc 을 선언 안해줘도 된다. 선언 안해주면 기본으로 사용하는 메시지 컨버터를 사용한다.
- @EnableWebMvc 을 선언하면 메시지 컨버터를 설정 해줘야한다.
- spring boot는 의존성을 추가하면 자동으로 설정이 된다. 웹 프로그래밍과 관련된 내용들이 자동으로 설정된다. 기본적인 메시지 컨버터들도 자동으로 등록된다.
spring boot에서 spring mvc
- spring boot로 spring mvc 웹 프로그래밍을 하려면 다음의 라이브러리 의존성을 추가하면 됩니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring boot 에서 암호화
- 암호화의 종류는 많다. 기본으로는 bcrypt알고리즘을 사용한다.
- PasswordEncoder가 '{암호화종류}암호화한내용' 으로 저장한다.
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
passwordEncoder.encode(user.getPassword());
Spring Boot Resource
- Gradle에서 dependencies에 추가한 webjars 파일은 META-INF에 있다.
- 해당 폴더의 파일을 사용하려면 WebMvcconfigurer 를 구현하고 addResourceHandlers를 구현한다.
Spring Boot에서 배포관련 설정
- Profile설정에 따라 다른 객체를 주입할 수 있다.
- application.yml 파일에 다음과 같이 기본으로 사용할 profile을 설정할 수 있다.
spring:
profiles:
active: local
my:
address: 192.168.1.100 <----- 공통설정
---
spring:
profiles: local
my:
address: 127.0.0.1 <---- local
---
spring:
profiles: real
my:
address: 192.168.1.120 <---- real
아래와 같이 @Value애노테이션을 이용하여 설정을 읽어왔다.
@Controller
@RequestMapping("/boards")
public class BoardController {
@Value("${my.address}")
private String address;
}
실행시에 프로그램 아규먼트를 줌으로써 원하는 profile로 실행할 수 있다.
java -jar 스프링부트애플리케이션.jar --spring.profiles.active=real
Profile설정에 따라 다른 객체를 주입할 수 있다.
package examples.boot.simpleboard.service;
public interface MyService {
public String getName();
}
ckage examples.boot.simpleboard.service.impl;
import examples.boot.simpleboard.service.MyService;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("real")
public class RealServiceImpl implements MyService {
@Override
public String getName() {
return "real";
}
}
package examples.boot.simpleboard.service.impl;
import examples.boot.simpleboard.service.MyService;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("local")
public class MyServiceImpl implements MyService {
@Override
public String getName() {
return "local";
}
}
@Autowired
MyService myService;
테이블 관련 설정
- 스프링 부트 애플리케이션이 테이블을 자동으로 생성 삭제 한다.
- chema.sql에는 테이블 삭제, 생성관련된 sql을 넣는다.
- data.sql도 애플리케이션이 실행될때마다 자동으로 실행한다.sample data를 넣는다.
- properties로 설정하면 spring.java.hibernate.ddl-auto=create-drop과 같다.
spring:
jpa:
hibernate:
ddl-auto: create-drop
재구동 없이 실행
- runtime('org.springframework.boot:spring-boot-devtools')가 있으면 build만 해도 된다.
- WAS를 재구동 안해도 된다.
Gradle 명령어로 작업 파일 만들기
- 실행가능한 jar파일 만들기
gradle jar
---> Build/libs/simpleboard-0.0.1.SNAPSHOT.jar
- 해당 jar 파일을 실행하기
cd build/libs/
jar -jar simpleboard-0.0.1.SNAPSHOT.jar
종료 : ctrl + c
Spring Security
- WebSecurityConfigurerAdapter 를 상속받아서 기능을 확장할 수 있다.
- compile('org.springframework.boot:spring-boot-starter-security')를 추가한다.
- 모든 경로를 로그인해야만 접근할 수 있다.
- 기본 사용자가 추가된다. 사용자 id : user, 암호는 부트 애플리케이션이 실행될 때 출력된다.
- 서블릿 필터는 빈이 아니다. WAS에서 생성해 준다.
- 스프링 컨테이너에는 10개이상의 시큐리티 관련 객체를 가지고 있는데 Spring을 개발한사람은 bean으로 관리되기를 원했다. 그래서 스프링 시큐리티가 제공해주는 필터를 넣어 준다. 이 필터는 스프링 관련 필터이기 때문에 시큐리티 관련 객체를 사용할 수 있다.
- 빈은 컨테이너가 관리하는 것을 말한다.
- 스프링이 빈으로 관리하는 필터가 있다.
- FilterChainProxy를 통해 스프링 빈에서 관리하는 필터를 사용한다.
- Spring Security Filter Chain을 추가 및 교체할 수 있다.
- Spring에서는 설정해야 할 부분이 많다. boot에서는 간단해진다.
- org.springframework.boot.autoconfigure.security.StaticResourceLocation 에 css, javascript, images, webjars, FAVICON 의 path가 정해져있다.
- 로그인을 검사하는 필터가 있다.
- 컨트롤러를 만드는게 아니다.
- path도 정해줘야한다.
- db정보와 검사해주는 객체가 있다.
- 권한(ROLE)을 부여해준다. 사용자아 권한은 1:N 관계
- 사용자가 가려고 했던 path로 이동해준다.
- 정보가 맞으면 세션을 저장해준다.]
- 필터에서 로그인 인증후 브라우저에게 path를 리다이렉트 한다.
- 로그아웃을 검사하는 필터가 있다.
- 컨트롤러를 만드는게 아니다
- 로그아웃후에 이동할 경로가 설정에 있다.
- 필터에서 로그아웃 처리후 브라우저에게 path를 리다이렉트 한다.
Spring Security 옵션
- 설정한 순서대로 옵션이 적용된다.
- AntPathRequestMatcher : Ant관련 path 설정
- permitAll() : 인증을 안하고 아무나 볼 수 있다.
- PathRequest.toStaticResources().atCommonLocations()).permitAll() : static 폴더 안에 폴더 기준에 맞게 파일을 넣고 그 폴더들은 인증을 안한다.(Spring boot에서 제공한다.)
- .anyRequest().fullyAuthenticated() : 등록한 url 인증이 있어야 접근 가능
- hasRole : 해당 롤 권한만 접근 할 수 있다.
- ..antMatchers("/h2-console/**").permitAll().and() .csrf().ignoringAntMatchers("/**") // .ignoringAntMatchers("/h2-console/**") .and().headers().frameOptions().disable() : H2 DB를 사용할때 설정
- .and().logout().permitAll() : 로그아웃은 누구나 접근 가능
- .and().formLogin() : 시큐리티가 제공하는 로그인 폼 사용
- .and().formLogin().loginPage("/uers/login").usernameParameter("id").passwordParameter("password") : 로그인 페이지를 변경하려고 할때 post방식으로 '/uers/login' 으로 input name을 id, password 로 설정하고 보내야한다. (get 방식은으로 요청하면 UI페이지로 가고 post방식으로 요청하면 스프링 시큐리티에게 요청된다.)
Spring Security 인증관련 로직 변경
- Spring boot는 UserDetailsService를 Implement 객체를 이미 가지고 있다.
- 인증관련 로직을 변경 하려면 UserDetailsService를 Implement 하는 클래스를 만들고.
@Componet로 등록한다.
- Principal를 통해 로그인 정보를 출력할 수 있다.
SecurityContextHolder.getContext().getAuthentication() 를 통해서도 출력할수 있다(Thread local로 구현되어 있고 시큐리티가 SecurityContextHolder에 넣어준다.)
- JdbcTokenRepositoryImpl 는 remember me 를 해주는 기능이다.
JPA 에서 1:N 관계 설정
- 패러다임 문제 해결 : OOP(객체 모델l)와 관계형 데이터베이스(관계 모델)는 같은 데이터를 표현하고 다루는 방법이 차이가 나는 문제를 해결
- users - user_role 테이블 (1 : N)
- users 테이블에서
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
private List UserRole roles;
JSON으로 반환할때 제거하고 보여준다.
@JsonIgnore
private String password;
pk로 지정하고 자동으로 값을 증가한다. 자동증가를 사용하지 않으면 key가 있는지 select문을 실행하기 때문에 성능이 느리다.
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
JPA 테이블 관계
- @OneToMany
- 양방향관계
- 1 : N -> N을 가진 테이블에 FK가 있다고 생각하면 편하다
- Many쪽이 주인공이다.
- 단방향일때는 oneToMany일때 one관계쪽다가 Many쪽의 Join컬럼을 적어준다. 키를 먼저 알지 못하기 때문에 insert, insert, update가 일어난다.
- 양방향 맵핑일때는 one관계쪽에는 @OneToMany 애노테이션에 mappedBy 옵션(Many쪽에 선언한 필드를 적는다)을 ,Many쪽에 @JoinColumn을 적는다. 키를 알기 때문에 insert, insert가 일어난다. 양방향으로 참조할 수 있도록 헬퍼 메소드를 만들어줘야한다. 성능적으로 단방향으로 구현하는 것보다 나을 수 있다. 서로 참조하지 않으면 무한 루프나 이상한 값이 들어갈 수 있다.
- @OneToMany에 mappedBy 옵션을 주고 @JoinColumn 컬럼을 주면 @JoinColumn 을 선언한 곳에 FK가 생긴다.
- 이 방법으로 모든 관계를 구현할 수 있다.
- 단방향 관계
- one관계를 가진 엔티티에 @JoinColumn을 선언한다.
- @OneToOne
- 양방향 관계
- 객체지향 관점으로는 어느쪽이 주인공(@JoinColumn)이 되든 상관이없다. @JoinColumn을 추가한곳에 N 관계를 맺게 나중에 변경할 수 있다.
- @ManyToMany(양방향 관계)
- 테이블 관점에서는 N:N는 존재하지 않는다. N : N은 1:N, N :1 관계로 변경해야한다.
- @ManyToMany, @JoinTable을 사용한다. 엔티티는 2개이지만 3개의 테이블을 만들어 준다.(이방법은 비추천)
- 1:N, N:1로 구현하자
JPA 사용하기
- JPQL
- Criteria Query, Query Dsl이 있다.
- @Query 어노테이션을 통해 JPQL을 이용해서 사용할 수 있다.
- @Param 어노테이션은 org.springframework.data에 포함되어있다.
- @Query("SELECT COUNT(b) FROM Board b WHERE b.user.name = :name")
- public Long countAllByUserName(@Param("name") String name);
- 페이징 처리
- 페이지 처리를 위해서는 Pageble 객체를 사용해야 한다. Pageble은 0이 시작이다.
- 페이징 처리를 위한 메소드는 Page<도메인객체> 반환하도록 한다. Page에는 검색된 결과, 검색된 결과의 견수, 전체 페이지수
- 페이지 처리를 위한 메소드는 Pageble 타입을 파라미터로 받아야 한다. 사용자가 해당 인터페이스를 구현한 객체를 파라미터로 전달해서 사용해야한다.
- Pageable pageable = PageRequest.of(....);
- PageRequest.of 안에는 파라미터(page값(시작이 0이다), 1page에 보여줄 수, 정렬규칙(sort객체))가 들어간다.
JPA @OneToMany fetch
- 1: N 관계일때 사용한다.
- FetchType.EAGER : 엔티티를 조회할때 주 테이블과 관계맺은 테이블을 조회한다.
- FetchType.LAZY : 실제 엔티티를 사용할때 관계맺은 테이블을 조회한다.
- FetchType.EAGER 로 다 선언했다면 하나의 테이블을 불렀을때 관련된 테이블을 다 조회하므로 좋지않다.
- SQL이 어떻게 조회되는지 확인하자.
- FetchType.LAZY을 리스트를 가지고 올때는 사용하지 말자
Gradle 명령어로 작업하기
- 실행가능한 jar파일 만들기
터미널에서 gradle jar 입력
---> Build/libs/simpleboard-0.0.1.SNAPSHOT.jar
- 해당 jar 파일을 실행하기
cd build/libs/
터미널에서 jar -jar simpleboard-0.0.1.SNAPSHOT.jar 입력
종료 : ctrl + c
Thymeleaf
- th:object= "${객체}" : 객체를 셋팅한다
- th:field= "*{객체의필드명}" : 가져온 객체의 필드를의 값을 가져온다.
- th:action ="@{path} " : contextpath를 path 앞에 붙여준다.
Thymeleaf layout
- ThymeleafAutoConfiguration 클래스의 내용을 보면 해당 라이브러리가 있을 경우에만 Layout을 지정할수 있다. gradle을 예로들면 다음과 같이 추가해야한다.
compile group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '2.3.0'
@Configuration
@ConditionalOnClass(name = "nz.net.ultraq.thymeleaf.LayoutDialect")
protected static class ThymeleafWebLayoutConfiguration {
@Bean
@ConditionalOnMissingBean
public LayoutDialect layoutDialect() {
return new LayoutDialect();
}
}
Thymeleaf 사용법
- th:if="${not #list.isEmpty(객체명)}" : 제공 하는 메소드를 이용해서 비어있지 않을 경우 tr태그를 사용
- th:each="board : ${list}" : list에 있는 내용을 하나씩 꺼내서 board에 대입하여 반복한다.
- <td th:text="${board.id}"></td> : board가 가지고 있는 id프로퍼티의 값을 td태그 안의 text값으로 출력한다. 만약 이름이 kim이 라면 <td>kim</td> 와 같은 결과가 출력된다.
- <td th:text="${#temporals.format(board.regdate, 'yyyy-MM-dd HH:mm')}"></td> ; regdate는 LocalDatetTime일때 타임 리프가 제공하는 #temporals.format()을 이용해서 적절한 포맷으로 변환할 수 있다.
- th:classappend : 조건에 따라서 클래스를 추가하 사용한다. 여러 조건을 넣을 경우 th:classappend="(조건) + (조건)" 으로 사용한다.
- <li th:each=" i : ${numbers.sequence( {pager.startPage}, {pager.endPage)}"></li> :
(시작번호, 끝번호)를 이용하면 위의 for문처럼 반복할 수 있다.
- <a th:href="@{/boards(page=${i}, searchStr=${pager.searchStr}, searchType=${pager.searchType})"> : URL주소에서 파라미터를 사용하는 방법
@{/boards(파라미터명=값, 파라미터명=값, 파라미터명=값)} 형태로 보낸다.
Hibernate
- 엔티티를 선언하면 Hibernate가 해당 엔티티를 상속하여 프록시 객체를 만들어 오버라이딩 하여 사용한다.
- 두개의 테이블이 관계를 맺고있을때 insert할떄 FK를 제외하고 insert한다음에 FK의 정보를 업데이트한다.
Hibernate: insert into users (id, email, name, password, regdate) values (null, ?, ?, ?, ?)
Hibernate: insert into user_roles (id, role_name) values (null, ?)
Hibernate: update user_roles set user_id=? where id=?
spring data method query
- 메소드 이름은 find로 시작한다.
public User findUserByEmail(String email)
user테이블에서 입력한 이메일에 맞는 조건을 찾는다.( where email = :email)
Message Converter
public void configureMessageConverters(List<HttpMessageConverter<?>> converters)
- Spring Boot 2.0에서 hibernate 5를 사용하고 엔티티를 반환하면 Exception이 발생한다. 기존의 컨버터가 변환을 못한다.
- 아래와 같이 컨버터를 생성한다
- 변환하기 위해 jackson이 제공하는 하이버네이트 모듈을 추가한다.
- 이렇게 했을 경우 LocalDateTime이 jsr310형식으로 출력되지 않는다.
- jackson이 제공하는 jsr310모듈을 추가한다.
// https://stackoverflow.com/questions/28862483/spring-and-jackson-how-to-disable-fail-on-empty-beans-through-responsebody
// https://github.com/FasterXML/jackson-datatype-hibernate/issues/87
// https://stackoverflow.com/questions/33727017/configure-jackson-to-omit-lazy-loading-attributes-in-spring-boot
@Override
public void configureMessageConverters(List> converters) {
// http://jsonobject.tistory.com/235 iso8601 형식으로 날짜 정보 출력하기
ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).modules(new JavaTimeModule()).build();
mapper.registerModule(new Hibernate5Module());
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter(mapper);
converter.setPrettyPrint(true);
converters.add(converter);
}
0 개의 댓글:
댓글 쓰기