기본 콘텐츠로 건너뛰기

10월, 2018의 게시물 표시

냉장고 가계부 프로젝트 38

Fridge 프로젝트의 Fridge, Food Entity 클래스의 @CreatedDate, @LastModifedDate 컬럼이 중복되어 나타나므로 상위클래스로 옮긴다. 상위 클래스는 BaseEntity 클래스로 선언하고 두개의 멤버 변수를 선언한다. Fridge, Food 클래스는 BaseEntity 를 상속받는다. @Getter @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public class BaseEntity { private @CreatedDate LocalDateTime createdDate; private @LastModifiedDate LocalDateTime lastModifiedDate; } Food 클래스는 다음과 같이 클래스 애너테이션이 선언된다. @NoArgsConstructor @Getter @Setter @ToString @Entity public class Food extends BaseEntity { ... } lombok @Getter, @Setter, @ToString 애너테이션을 선언하고 LocalDateTime createDate, LocalDateTime lastModifiedDate 필드는 제거한다. Fridge 클래스도 동일하게 수정한다. UI 서버에서 로그인을 한 뒤에, 비밀번호 변경과 회원탈퇴 기능은 제공되지만, 비밀번호를 잊어버렸을 경우 로그인을 할 수 없는 문제가 있다. 따라서, 비밀번호 찾기 기능을 제공한다. 비밀번호 찾기 기능은 가입시 입력한 이메일을 입력하면 해당 이메일로 인증코드가 발송되고, 해당 메일에서 인증코드 링크를 클릭하면 비밀번호를 변경할 수 있는 페이지를 제공한다. 인증코드는 Member 서비스에서 관리하는게 적합한데, 개별 회원정보가 Member 서비스에서 관리되기 때문이다. Member 프로젝트의 스키마를 수정한다. CREATE TABLE member ( id BIGIN

냉장고 가계부 프로젝트 37

fridge 프로젝트도 member 프로젝트와 같이 Spring Data Rest 를 사용하여 코드를 정리한다. response 본문에 id 가 표시되도록 RepositoryRestConfig 클래스를 작성한다. @Configuration public class RepositoryRestConfig extends RepositoryRestConfigurerAdapter { @Override public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { config.exposeIdsFor(Fridge.class, Food.class); } } Fridge, Food 클래스 두 엔티티의 Id를 노출한다. FridgeRepository, FoodRepository 인터페이스에 @RepositoryRestResource 애너테이션을 선언한다. @RepositoryRestResource public interface FridgeRepository extends JpaRepository<Fridge, Integer> { Optional<Fridge> findByUserId(@Param("userId") Long userId); } @RepositoryRestResource public interface FoodRepository extends JpaRepository<Food, Long> { } FridgeRepository의 findByUserId 메서드의 파라미터에 @Param("userId") 애너테이션을 선언한다. findByUserId 메서드는 fridges/search/findByUserId GET URL 형태로 제공된다. @Param("userId") 를 선언하면 QueryString userId를 키로 사용

냉장고 가계부 프로젝트 36

member 프로젝트에서는 간단한 CRUD 기능만을 가지므로 Spring Data Rest를 사용해서 코드의 양을 줄일 수 있다. Spring Data Rest와 Spring HATEOAS, Spring Data JPA 를 사용한다. pom.xml 에 Spring Data Rest 의존성을 추가한다. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> Spring Data Rest는 Repository인터페이스에 @RepositoryRestResource 애너테이션을 추가하기만 하면 CRUD 관련 endpoint를 작성해준다. MemberRepository 인터페이스에 다음과 같이 애너테이션을 추가한다. @RepositoryRestResource public interface MemberRepository extends JpaRepository<Member, Long> { Optional<Member> findByUsername(String username); } Spring Data Rest는 {repository} GET,POST {repository}/{id} GET,PUT,PATCH,DELETE, {repository}/search GET 과 같은 CRUD endpoint를 작성해준다. MemberRepository는 members, members/{id}, members/search 와 같다. (더 상세한 내용은  Spring Data Rest Reference  를 참고.) Spring Data Rest 관련 설정은 yaml 을 통해서도 가능하고, 별도의 Configuration 클래스를 작성할 수도 있다. Member 프로젝트같은 경우 회원의 식별키를

냉장고 가계부 프로젝트 35

Spring security 프레임워크에서 form login 방식으로 인증할 경우 Remember-Me 서비스를 제공할 수 있다. Remember-Me 서비스를 이용하면 쿠키에 인증토큰을 저장해두고 일정기간동안(default: 2주) 쿠키에 토큰이 있다면 인증처리를 해준다. 기본적으로 별도의 설정이 없다면 세션은 30분 만료시간으로 정해져있다.(WAS 마다 틀리다.) 세션 시간이 만료되면 세션에 저장된 정보가 날라가므로 다시 로그인을 해야한다. Remeber-Me 기능을 이용하면, form login에서 인증을 성공적으로 진행하는 경우 임의의 인증토큰을 생성해서 쿠키에 저장해두고 쿠키가 만료되기 전까지 쿠키값을 확인해서 인증을 진행한다. 별도의 설정을 하지 않으면 in-memory 기반의 쿠키저장소를 이용해서 쿠키 토큰인증을 하며, JDBC같은 저장소를 별도로 사용할 수도 있다. WebSecurityConfig 클래스에 다음과 같이 간단한 설정만으로 remember-me 기능을 추가할 수 있다. @Configuration @EnableWebSecurity @RequiredArgsConstructor public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final MemberUserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/signup").permitAll() .requestMatchers(PathRequest.toStaticResources().atCommonLocations()

냉장고 가계부 프로젝트 34

회원가입과 로그인, 로그아웃 기능은 구현했으니, 이제는 비밀번호 변경과 회원탈퇴기능을 구현한다. member 프로젝트의 MemberService 클래스에 탈퇴 메서드를 추가한다. @Service @Transactional @RequiredArgsConstructor public class MemberService { private final MemberRepository repository; public Member save(Member member) { return repository.save(member); } public void withdraw(long id) { repository.deleteById(id); } } withdraw 메서드는 파라미터로 회원 아이디를 입력받아서 repository의 deleteById 메서드를 호출한다. MemberController 클래스에서 비밀번호 변경, 회원탈퇴 endpoint를 작성한다. @RestController @RequestMapping("/members") @RequiredArgsConstructor public class MemberController { ... @PutMapping("/{id}") ResponseEntity<?> changeMemberPassword(@PathVariable final long id, @RequestBody final Member memberRequest) throws URISyntaxException { Member updatedMember = repository.findById(id) .map(member -> { member.setPassword(memberRequest.getPassword());

냉장고 가계부 프로젝트 33

Spring security 의 권한 구분과 계정잠금/만료 정책관련 필드 정보를 제공하기 위해 Member 서버를 수정한다. CREATE TABLE member ( id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY, username VARCHAR(256) NOT NULL, password VARCHAR(128) NOT NULL, account_non_expired BOOLEAN NOT NULL DEFAULT TRUE, account_non_locked BOOLEAN NOT NULL DEFAULT TRUE, credentials_non_expired BOOLEAN NOT NULL DEFAULT TRUE, enabled BOOLEAN NOT NULL DEFAULT TRUE ); ALTER TABLE member ADD CONSTRAINT UK_member_username UNIQUE (username); CREATE TABLE member_authorities ( member_id BIGINT NOT NULL, authority VARCHAR(32) NOT NULL ); member 테이블에는 account_non_expired, account_non_locked, credentials_non_expired, enabled 네 개의 Boolean 컬럼이 추가되고 Default 값은 true로 지정한다. 그리고 권한구분값을 저장할 member_authorities 테이블을 생성한다. member_authorities 테이블에는 member 테이블의 식별키 id값을 참조하는 member_id 컬럼과 문자열로 저장되는 권한구분 authority 컬럼으로 구성된다. @Data @NoArgsConstructor @Entity public class Member { @Id @Gene

냉장고 가계부 프로젝트 32

fridge-member 프로젝트의 회원 가입을 위한 API를 추가한다. UI 서버에서 회원가입을 진행하면 아이디와 패스워드가 요청바디에 담겨서 member API를 POST 방식으로 호출한다. JPA repository 의 save 메서드를 호출하는 서비스 로직을 담는 MemberService 클래스를 우선 작성한다. @Service @Transactional @RequiredArgsConstructor public class MemberService { private final MemberRepository repository; public Member save(Member member) { return repository.save(member); } } 클래스에는 Service 애너테이션이 선언되고, Transactional 애너테이션이 선언되어 있다. Lombok 라이브러리의 RequiredArgsConstructor 애너테이션을 선언해서 repository 의존성을 생성자에서 주입한다. save 메서드는 Member 클래스를 파라미터로 전달받고 생성된 Member 객체를 리턴한다. 메서드 내부에서는 repository 의 save메서드를 호출한다. UI 서버에서 호출하는 endpoint인 컨트롤러 메서드를 MemberController 클래스에 추가한다. @RestController @RequestMapping("/members") @RequiredArgsConstructor public class MemberController { private final MemberRepository repository; private final MemberResourceAssembler assembler; private final MemberService service; ... @PostMapping ResponseEn