2018년 4월 20일 금요일

Spring Boot 2.0에서 Gradle,Spring Security, JPA, thymeleaf, Hibernate, spring data, Message Converter

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과 같다.
    • ddl-auto : 테이블 생성 및 삭제여부
    • 
      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

  • spring 을 이용해서 메시지 컨버터를 등록하는 방법
    http://www.baeldung.com/spring-httpmessageconverter-rest
  • spring boot에서의 메시지 컨버터 설정
    • java config파일 중 WebMvcConfigurer를 구현하는 클래스에서 다음의 메소드를 오버라이딩한다.
    • converters 에 메시지를 컨버터를 등록하면 됩니다.
    • 
      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);
      }
    Share:

    0 개의 댓글:

    댓글 쓰기