diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/build.gradle b/build.gradle index 15b77ef..6f1f981 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,12 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.0.1' - id 'io.spring.dependency-management' version '1.1.0' + id 'org.springframework.boot' version '2.7.8' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' } -group = 'com.dku' +group = 'com.project' version = '0.0.1-SNAPSHOT' -sourceCompatibility = '17' +sourceCompatibility = '11' configurations { compileOnly { @@ -22,11 +22,19 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' - compileOnly 'org.projectlombok:lombok' + implementation 'org.jetbrains:annotations:23.0.0' + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' + + // jwt + implementation 'io.jsonwebtoken:jjwt:0.9.1' + + //validation + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.slf4j:slf4j-api:1.7.31' } tasks.named('test') { diff --git a/settings.gradle b/settings.gradle index 73e37fd..e46002a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -rootProject.name = 'springstudy' +rootProject.name = 'carrot' diff --git a/src/main/java/com/dku/springstudy/SpringStudyApplication.java b/src/main/java/com/dku/springstudy/SpringStudyApplication.java deleted file mode 100644 index ef164c9..0000000 --- a/src/main/java/com/dku/springstudy/SpringStudyApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.dku.springstudy; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class SpringStudyApplication { - - public static void main(String[] args) { - SpringApplication.run(SpringStudyApplication.class, args); - } - -} diff --git a/src/main/java/com/project/carrot/CarrotApplication.java b/src/main/java/com/project/carrot/CarrotApplication.java new file mode 100644 index 0000000..9a5a5df --- /dev/null +++ b/src/main/java/com/project/carrot/CarrotApplication.java @@ -0,0 +1,15 @@ +package com.project.carrot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@EnableJpaAuditing //JpaAuditing을 활성화 해주는 기능 +@SpringBootApplication +public class CarrotApplication { + + public static void main(String[] args) { + SpringApplication.run(CarrotApplication.class, args); + } + +} diff --git a/src/main/java/com/project/carrot/CustomException.java b/src/main/java/com/project/carrot/CustomException.java new file mode 100644 index 0000000..5644505 --- /dev/null +++ b/src/main/java/com/project/carrot/CustomException.java @@ -0,0 +1,11 @@ +package com.project.carrot; + +import com.project.carrot.domain.ErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class CustomException extends RuntimeException{ + ErrorCode errorCode; +} diff --git a/src/main/java/com/project/carrot/MyInterceptor.java b/src/main/java/com/project/carrot/MyInterceptor.java new file mode 100644 index 0000000..be64e32 --- /dev/null +++ b/src/main/java/com/project/carrot/MyInterceptor.java @@ -0,0 +1,57 @@ +package com.project.carrot; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.project.carrot.domain.PostSaveDTO; +import com.project.carrot.domain.SuccessResponseDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Slf4j //로깅에 사용하는 어노테이션 +@RequiredArgsConstructor +@Component +public class MyInterceptor implements HandlerInterceptor { + private final ObjectMapper objectMapper; + @Override + public void afterCompletion( + HttpServletRequest request, + HttpServletResponse response, + Object object, + Exception ex + ) throws Exception { + //wrapping 된 Response 가 넘어 올 것이다. + final ContentCachingResponseWrapper cachingResponse = (ContentCachingResponseWrapper) response; + //200번대 응답이 아닌 경우 Interceptor 를 거치지 않는다. + if (!String.valueOf(response.getStatus()).startsWith("2")) { + return; + } + //Json 형식의 응답값만 수정 - @ResponseBody를 통해 Object 반환은 JSON으로 넘어옴. + if (cachingResponse.getContentType() != null && (cachingResponse.getContentType().contains("application/json"))) { + if (cachingResponse.getContentAsByteArray().length != 0) { + //String 변환 ex){"key":"value"} + String body = new String(cachingResponse.getContentAsByteArray()); + + //Object형식으로 변환 -> Response에 꽂아주기 위함. + Object data = objectMapper.readValue(body, Object.class); + + //ResponseEntity 생성 + SuccessResponseDTO objectResponseDTO = new SuccessResponseDTO<>(data); + + //String 변환 -> Byte로 쓰기 위함. + String wrappedBody = objectMapper.writeValueAsString(objectResponseDTO); + + //flush 버퍼에 있는 데이터를 처리함. + cachingResponse.resetBuffer(); + + //덮어쓰기 + cachingResponse.getOutputStream().write(wrappedBody.getBytes(), 0, wrappedBody.getBytes().length); + log.info("Response Body : {}", wrappedBody); + } + } + } +} diff --git a/src/main/java/com/project/carrot/Service/MemberItemService.java b/src/main/java/com/project/carrot/Service/MemberItemService.java new file mode 100644 index 0000000..5d87ba9 --- /dev/null +++ b/src/main/java/com/project/carrot/Service/MemberItemService.java @@ -0,0 +1,32 @@ +package com.project.carrot.Service; + +import com.project.carrot.domain.ItemDTO; +import com.project.carrot.domain.MemberItem; +import com.project.carrot.repository.MemberItemRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +public class MemberItemService { + /* + * 상품 등록 페이지 구현 + * */ + + public final MemberItemRepository memberItemRepository; + + @Transactional + public MemberItem append(MemberItem memberItem){ + return memberItemRepository.save(memberItem); + } + + @Transactional + public MemberItem saled(Long itemId, Long userId){ + MemberItem memberItem=memberItemRepository.findByItemId(itemId) + .orElseThrow(()->new RuntimeException("존재하지 않는 유저입니다.")); + //memberItem=memberItem.toBuilder().ItemForSale(false).build(); + memberItem.update(false,userId); + + System.out.println(memberItem.toString()); + return memberItem; + } +} diff --git a/src/main/java/com/project/carrot/Service/MemberService.java b/src/main/java/com/project/carrot/Service/MemberService.java new file mode 100644 index 0000000..5617186 --- /dev/null +++ b/src/main/java/com/project/carrot/Service/MemberService.java @@ -0,0 +1,47 @@ +package com.project.carrot.Service; + +import com.project.carrot.domain.Member; +import com.project.carrot.domain.PostSaveDTO; +import com.project.carrot.repository.MemberRepository; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + + +@RequiredArgsConstructor +@Transactional//JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행되어야만 한다. +public class MemberService implements UserDetailsService { + /* + 회원가입, 로그인 기능 개발 + */ + private final MemberRepository memberRepository; + + public Member join(Member member){ + memberRepository.findByEmail(member.getUserEmail()) + .ifPresent(m->{throw new IllegalStateException("이미 존재하는 이메일입니다.");}); + + return memberRepository.save(member); + } + + //컨트롤러에서 역할 수행하게 되어 주석처리 + /*public Member Login(String email, String password){ + Member findMember = memberRepository.findByEmail(email) + .orElseThrow(() -> new IllegalStateException("이메일이 일치하지 않습니다.")); + + if(findMember.getUserPass() != password) throw new IllegalStateException("패스워드가 일치하지 않습니다."); + + return findMember; + }*/ + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return memberRepository.findByEmail(username) + .orElseThrow(()->new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); + } +} diff --git a/src/main/java/com/project/carrot/config/InterceptorConfig.java b/src/main/java/com/project/carrot/config/InterceptorConfig.java new file mode 100644 index 0000000..b4e9d4c --- /dev/null +++ b/src/main/java/com/project/carrot/config/InterceptorConfig.java @@ -0,0 +1,19 @@ +package com.project.carrot.config; + +import com.project.carrot.MyInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration //설정 파일이고 Bean을 등록할 것이다. +@RequiredArgsConstructor +public class InterceptorConfig implements WebMvcConfigurer { + private final MyInterceptor myInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry){ + registry.addInterceptor(myInterceptor) + .addPathPatterns("/**"); //localhost:8080 의 이하 경로 + } +} diff --git a/src/main/java/com/project/carrot/config/SecurityConfig.java b/src/main/java/com/project/carrot/config/SecurityConfig.java new file mode 100644 index 0000000..c1e03c2 --- /dev/null +++ b/src/main/java/com/project/carrot/config/SecurityConfig.java @@ -0,0 +1,47 @@ +package com.project.carrot.config; + +import com.project.carrot.security.JwtAuthenticationFilter; +import com.project.carrot.security.JwtTokenProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + private final JwtTokenProvider jwtTokenProvider; + + // authenticationManager를 Bean 등록합니다. + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + + http.csrf().disable(); + //http.httpBasic().disable(); // 일반적인 루트가 아닌 다른 방식으로 요청시 거절, header에 id, pw가 아닌 token(jwt)을 달고 간다. 그래서 basic이 아닌 bearer를 사용한다. + http.httpBasic().disable() + .authorizeRequests()// 요청에 대한 사용권한 체크 + .antMatchers("/test").authenticated() + //.antMatchers("/admin/**").hasRole("ADMIN") + .antMatchers("/user/**").hasRole("USER") + .antMatchers("/**").permitAll() + .and() + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), + UsernamePasswordAuthenticationFilter.class); // JwtAuthenticationFilter를 UsernamePasswordAuthenticationFilter 전에 넣는다 + // + 토큰에 저장된 유저정보를 활용하여야 하기 때문에 CustomUserDetailService 클래스를 생성합니다. + http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + + + } +} + diff --git a/src/main/java/com/project/carrot/config/SpringConfig.java b/src/main/java/com/project/carrot/config/SpringConfig.java new file mode 100644 index 0000000..a5abbe1 --- /dev/null +++ b/src/main/java/com/project/carrot/config/SpringConfig.java @@ -0,0 +1,42 @@ +package com.project.carrot.config; + +import com.project.carrot.Service.MemberItemService; +import com.project.carrot.Service.MemberService; +import com.project.carrot.repository.JPAMemberItemRepository; +import com.project.carrot.repository.JPAMemberRepository; +import com.project.carrot.repository.MemberItemRepository; +import com.project.carrot.repository.MemberRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.persistence.EntityManager; +import javax.sql.DataSource; + +@Configuration +public class SpringConfig { + + private final DataSource dataSource; + private final EntityManager em; + + public SpringConfig(DataSource dataSource, EntityManager em) { + this.dataSource = dataSource; + this.em = em; + } + + + @Bean + public MemberService memberService(){ + return new MemberService(memberRepository()); + } + + @Bean + public MemberRepository memberRepository(){ + return new JPAMemberRepository(em); + } + + @Bean + public MemberItemService memberItemService(){return new MemberItemService(memberItemRepository());} + + @Bean + public MemberItemRepository memberItemRepository(){return new JPAMemberItemRepository(em);} + } diff --git a/src/main/java/com/project/carrot/controller/ControllerAdvisor.java b/src/main/java/com/project/carrot/controller/ControllerAdvisor.java new file mode 100644 index 0000000..9a30fa4 --- /dev/null +++ b/src/main/java/com/project/carrot/controller/ControllerAdvisor.java @@ -0,0 +1,19 @@ +package com.project.carrot.controller; + +import com.project.carrot.CustomException; +import com.project.carrot.domain.ExceptionDTO; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice //@ControllerAdvice + @ResponseBody +//@ControllerAdvice는 컨트롤러에서 발생하는 에러를 처리 //SecurityFilter에서 발생하는 예외는 스프링의 것이 아니므로 처리하지 못함 +//@ResponseBody: 응답값 String->Stirng, Object->Json 형식으로 반환해준다. +//@ExceptionHandler: 특정 예외처리를 지정하여 관리 +public class ControllerAdvisor { + @ExceptionHandler(value = {CustomException.class}) + protected ResponseEntity exceptionHandler(CustomException e){ + return ResponseEntity.status(e.getErrorCode().getStatus()) + .body(new ExceptionDTO(e)); + } +} diff --git a/src/main/java/com/project/carrot/controller/JWTController.java b/src/main/java/com/project/carrot/controller/JWTController.java new file mode 100644 index 0000000..fdacd13 --- /dev/null +++ b/src/main/java/com/project/carrot/controller/JWTController.java @@ -0,0 +1,72 @@ +package com.project.carrot.controller; + +import com.project.carrot.MyInterceptor; +import com.project.carrot.domain.Member; +import com.project.carrot.Service.MemberService; +import com.project.carrot.domain.PostSaveDTO; +import com.project.carrot.repository.MemberRepository; +import com.project.carrot.security.JwtTokenProvider; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.validator.constraints.Email; +import org.springframework.boot.autoconfigure.kafka.KafkaProperties; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; +import java.util.Optional; + + +@Slf4j //로깅할때 쓰는 어노테이션 +@RestController +@RequiredArgsConstructor +public class JWTController { + + private final MemberRepository memberRepository; + private final MemberService memberService; + private final JwtTokenProvider jwtTokenProvider; + + + @PostMapping(value = "/join") + public Member joinUser(@RequestBody PostSaveDTO postSaveDTO) { + log.info("회원가입 시도"); + Member member = postSaveDTO.toEntity(); + return memberService.join(member); + } + + @PostMapping(value = "/login") + public String login(@RequestBody Map user) { + Member member=memberRepository.findByEmail(user.get("EMAIL")) + .orElseThrow(()->new IllegalArgumentException("가입되지 않은 이메일입니다.")); + + log.info(user.get("PASS")); + log.info(member.getPassword()); + + if(!user.get("PASS").equals(member.getPassword())) + throw new IllegalArgumentException("비밀번호가 일치하지 않습니다."); + + return jwtTokenProvider.createToken(member.getUserEmail(), member.getRoles()); + } + @PostMapping(value = "/findId") + public String findId(@RequestBody Map userTel) { + Member member = memberRepository.findByUserTel(userTel.get("userTel")) + .orElseThrow(() -> new IllegalArgumentException("계정이 존재하지 않습니다.")); + + return member.getUserEmail(); + } + + @PostMapping(value = "/findPassword")//전화번호와 이메일을 입력 + public String findPassword(@RequestBody Map userInfo){ + Member member1=memberRepository.findByUserTel(userInfo.get("userTel")) + .orElseThrow(()->new RuntimeException("계정이 존재하지 않습니다.")); + + Member member2=memberRepository.findByEmail(userInfo.get("Email")) + .orElseThrow(()->new RuntimeException("계정이 존재하지 않습니다.")); + + if(!member1.equals(member2)) + throw new RuntimeException("계정이 존재하지 않습니다."); + + return member1.getPassword(); + } +} \ No newline at end of file diff --git a/src/main/java/com/project/carrot/controller/MemberItemController.java b/src/main/java/com/project/carrot/controller/MemberItemController.java new file mode 100644 index 0000000..cb6f4c2 --- /dev/null +++ b/src/main/java/com/project/carrot/controller/MemberItemController.java @@ -0,0 +1,90 @@ +package com.project.carrot.controller; + +import com.project.carrot.MyInterceptor; +import com.project.carrot.Service.MemberItemService; +import com.project.carrot.domain.*; +import com.project.carrot.repository.MemberItemRepository; +import com.project.carrot.repository.MemberRepository; +import com.project.carrot.security.JwtTokenProvider; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@Slf4j +@RequiredArgsConstructor +public class MemberItemController { + private final MemberItemRepository memberItemRepository; + private final MemberItemService memberItemService; + private final JwtTokenProvider jwtTokenProvider; + private final MemberRepository memberRepository; + + + @GetMapping(value = "/item") //추후에 /user/item으로 변경해서 권한 있는 사람만 가능하게 + public List showItemList() { + return memberItemRepository.findAll(); + } + + + @PostMapping(value = "/item/sellItem") + public MemberItem sellItem(@RequestBody ItemDTO itemDTO) { + MemberItem memberItem = itemDTO.toEntity(); + Member member = memberItemRepository.findByUserId(memberItem.getUserId()) + .orElseThrow(()-> new IllegalArgumentException("유저 정보가 없습니다.")); + + log.info(member.getUserEmail()); + log.info("상품 추가"); + + return memberItemRepository.save(memberItem); + } + + @RequestMapping(value="/item/buyItem") //URL을 통해 memberItem 반환 + public MemberItem buyItemPage(@RequestParam("itemId") Long id) { + log.info("인자의 값", id); + MemberItem memberItem = memberItemRepository.findByItemId(id) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 ItemID")); + + return memberItem; + } + + @PostMapping(value = "/item/buyItem") + public MemberItem buyItem(@RequestParam("itemId") Long id,@RequestBody PostSaveDTO postSaveDTO) { + MemberItem memberItem = memberItemRepository.findByItemId(id) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 ItemID")); + try { + if (!memberItem.getItemForSale()) { + log.info("이미 판매된 상품입니다."); + } + } + catch (Exception e){ + e.getMessage(); + } + Member member=memberRepository.findByEmail(postSaveDTO.getEMAIL()) + .orElseThrow(()->new RuntimeException("유저 정보가 존재하지 않습니다.")); + + return memberItemService.saled(id, memberItem.getUserId()); + } + + //mypage + @GetMapping(value="/mypage/sellItem") + public List mySellList(@RequestParam("userId") Long id){ + Member member=memberRepository.findById(id) + .orElseThrow(()->new IllegalArgumentException("유저 정보가 존재하지 않습니다.")); + log.info("member",member.getUserId()); + List memberItemList = memberItemRepository.findAllByUserId(member.getUserId()); + + return memberItemList; + } + + @GetMapping(value="/mypage/buyItem") + public List myBuyList(@RequestParam("userId") Long id) { + Member member = memberRepository.findById(id) + .orElseThrow(() -> new RuntimeException("유저 정보가 존재하지 않습니다.")); + log.info("member",member.getUserId()); + List memberItemList = memberItemRepository.findAllByBuyUserId(member.getUserId()); + + return memberItemList; + } +} diff --git a/src/main/java/com/project/carrot/domain/ErrorCode.java b/src/main/java/com/project/carrot/domain/ErrorCode.java new file mode 100644 index 0000000..ae930be --- /dev/null +++ b/src/main/java/com/project/carrot/domain/ErrorCode.java @@ -0,0 +1,14 @@ +package com.project.carrot.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ErrorCode { + CUSTOM_ERROR(HttpStatus.BAD_REQUEST,"잘못된 요청입니다 :)"); + + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/com/project/carrot/domain/ExceptionDTO.java b/src/main/java/com/project/carrot/domain/ExceptionDTO.java new file mode 100644 index 0000000..fd59015 --- /dev/null +++ b/src/main/java/com/project/carrot/domain/ExceptionDTO.java @@ -0,0 +1,23 @@ +package com.project.carrot.domain; + +import com.project.carrot.CustomException; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@AllArgsConstructor +@Getter +public class ExceptionDTO { //예외처리 DTO 생성 + private final Boolean success; + private final HttpStatus status; + private final String code; + private final String message; + + public ExceptionDTO(CustomException e){ //예외가 발생하면 나타나는 것들 + this.success=false; + this.status=e.getErrorCode().getStatus(); + this.code=e.getErrorCode().name(); + this.message=e.getErrorCode().getMessage(); + } + +} diff --git a/src/main/java/com/project/carrot/domain/ItemDTO.java b/src/main/java/com/project/carrot/domain/ItemDTO.java new file mode 100644 index 0000000..eb910eb --- /dev/null +++ b/src/main/java/com/project/carrot/domain/ItemDTO.java @@ -0,0 +1,42 @@ +package com.project.carrot.domain; + +import com.project.carrot.repository.MemberItemRepository; +import lombok.Builder; +import lombok.Data; + + +@Data +public class ItemDTO { + private Long USERID; + private String TITLE; + private String CONTENT; + private int PRICE; + private String CATEGORY; + private Member member; + @Builder.Default + private boolean ITEMFORSALE=true; + + private MemberItemRepository memberItemRepository; + + + @Builder + public ItemDTO(Long USERID,String TITLE,String CONTENT, int PRICE,String CATEGORY){ + this.USERID=USERID; + this.TITLE=TITLE; + this.CONTENT=CONTENT; + this.CATEGORY=CATEGORY; + this.PRICE=PRICE; + } + + public MemberItem toEntity(){ + return MemberItem.builder() + .UserId(USERID) + .ItemTitle(TITLE) + .ItemContent(CONTENT) + .ItemPrice(PRICE) + .ItemCategory(CATEGORY) + //.member(memberItemRepository.findByUserId(USERID).orElseThrow(()->new RuntimeException("존재하지 않는 member"))) + .build(); + } + +} diff --git a/src/main/java/com/project/carrot/domain/Member.java b/src/main/java/com/project/carrot/domain/Member.java new file mode 100644 index 0000000..06e208b --- /dev/null +++ b/src/main/java/com/project/carrot/domain/Member.java @@ -0,0 +1,93 @@ +package com.project.carrot.domain; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.*; +import org.apache.catalina.User; +import org.springframework.context.annotation.Primary; +import org.springframework.lang.Nullable; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@AllArgsConstructor +@RequiredArgsConstructor +@Entity //객체라는 것을 알림 +@ToString +@Builder +public class Member implements UserDetails{ + @Id //Id는 pk + @GeneratedValue(strategy = GenerationType.AUTO) + private Long UserId; + private String UserEmail; + private String UserPass; + private String UserTel; + private String UserName; + private String NickName; + private String Address; + @Builder.Default private double MannerTemp=36.5; + @Builder.Default + private String ProfilePhotoURL=null; + + @ElementCollection(fetch = FetchType.EAGER) + @Builder.Default + private List roles = new ArrayList<>(); + + @Override + public Collection getAuthorities(){ + return this.roles.stream() + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + } + + @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL) + private Collection memberItem; + + @Override + public String getPassword() { + return UserPass; + } + + @Override + public String getUsername() { + return UserEmail; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + +// public static MemberBuilder builder(PostSaveDTO postSaveDTO) { +// return new MemberBuilder() +// .UserEmail(postSaveDTO.getEMAIL()) +// .UserPass(postSaveDTO.getPASS()) +// .UserTel(postSaveDTO.getTEL()) +// .UserName(postSaveDTO.getUSERNAME()) +// .NickName(postSaveDTO.getNICKNAME()) +// .Address(postSaveDTO.getADDRESS()); +// } + +} + diff --git a/src/main/java/com/project/carrot/domain/MemberItem.java b/src/main/java/com/project/carrot/domain/MemberItem.java new file mode 100644 index 0000000..5d29c5d --- /dev/null +++ b/src/main/java/com/project/carrot/domain/MemberItem.java @@ -0,0 +1,48 @@ +package com.project.carrot.domain; + +import lombok.*; +import org.springframework.lang.Nullable; + +import javax.persistence.*; + +@Getter +@AllArgsConstructor +@NoArgsConstructor //필요할 듯 +@Entity //객체라는 것을 알림 +@ToString +@Builder +public class MemberItem { + @Id + @GeneratedValue + private Long ItemId; + private String ItemTitle; + private String ItemContent; + private int ItemPrice; + @Builder.Default + private String ItemApplyDate=null; //객체 생성 후에 JPA Auditing을 이용해서 반환할 것 + @Builder.Default + private Boolean ItemForSale=true; //올리면 판매하는 상황이기 때문에 + //private String ItemLocation; //Member에서 가져와야하는데 아직 구현하지 않았음 + @Builder.Default + private int ItemEmphathy=0; //공감을 클릭하면 올라갈 수 있게 0으로 초기화 + private String ItemCategory; + //private Member ItemSeller; //ManyToOne과 OneToMany 공부 후 결정 + @Builder.Default + private int ItemChatting=0; //채팅한 횟수 + @Builder.Default + private int ItemViews=0; //조회한 횟수 + @Builder.Default + private String ItemUpdated=null; + @Builder.Default + private String ItemDeleted=null; + //@Column(name="UserId",insertable = false,updatable = false) + private Long UserId; + @Builder.Default + private Long BuyUserId=null; + + public void update(boolean ItemForSale, Long BuyUserId){ + this.ItemForSale=ItemForSale; + this.BuyUserId=BuyUserId; + } + +} \ No newline at end of file diff --git a/src/main/java/com/project/carrot/domain/PostSaveDTO.java b/src/main/java/com/project/carrot/domain/PostSaveDTO.java new file mode 100644 index 0000000..7b7aa26 --- /dev/null +++ b/src/main/java/com/project/carrot/domain/PostSaveDTO.java @@ -0,0 +1,43 @@ +package com.project.carrot.domain; + +import lombok.*; + +import java.util.Collections; + +@Data +public class PostSaveDTO { + private String EMAIL; + private String PASS; + private String TEL; + private String USERNAME; + private String NICKNAME; + private String ADDRESS; + //private String + + //final KafkaProperties.Admin ADMIN = Admin.일반회원; 권한설정 필요할 경우 설정 + + + @Builder + public PostSaveDTO(String EMAIL,String PASS,String TEL,String USERNAME,String NICKNAME,String ADDRESS){ + this.EMAIL=EMAIL; + this.PASS=PASS; + this.TEL=TEL; + this.USERNAME=USERNAME; + this.NICKNAME=NICKNAME; + this.ADDRESS=ADDRESS; + } + + public Member toEntity(){ + return Member.builder() + .UserEmail(EMAIL) + .UserPass(PASS) + .UserTel(TEL) + .UserName(USERNAME) + .NickName(NICKNAME) + .Address(ADDRESS) + .roles(Collections.singletonList("ROLE_USER")) + .build(); + } +} + + diff --git a/src/main/java/com/project/carrot/domain/SuccessResponseDTO.java b/src/main/java/com/project/carrot/domain/SuccessResponseDTO.java new file mode 100644 index 0000000..bd129cf --- /dev/null +++ b/src/main/java/com/project/carrot/domain/SuccessResponseDTO.java @@ -0,0 +1,19 @@ +package com.project.carrot.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.lang.Nullable; + +@AllArgsConstructor +@Getter +public class SuccessResponseDTO { //응답값을 통일하기 위한 DTO 생성 + private final Boolean success; + private final T data; + + public SuccessResponseDTO(@Nullable T data){ + this.success=true; + this.data=data; + } +} + +//다른 곳에서는 resultCode까지 반환했음 \ No newline at end of file diff --git a/src/main/java/com/project/carrot/domain/Timestamped.java b/src/main/java/com/project/carrot/domain/Timestamped.java new file mode 100644 index 0000000..ed06a86 --- /dev/null +++ b/src/main/java/com/project/carrot/domain/Timestamped.java @@ -0,0 +1,22 @@ +package com.project.carrot.domain; + +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.time.LocalDate; + +@MappedSuperclass //데이터베이스에 자동으로 생성하게 도와줌 +@Getter +@EntityListeners(AuditingEntityListener.class)//생성과 변경 시간을 자동으로 업데이트 해줌 +public class Timestamped { + @CreatedDate + private LocalDate createAt; + + @LastModifiedBy + private LocalDate modifiedAt; +} +//이후 CarrotApplication에 @EnableJpaAuditing 해줘야 함 diff --git a/src/main/java/com/project/carrot/repository/JPAMemberItemRepository.java b/src/main/java/com/project/carrot/repository/JPAMemberItemRepository.java new file mode 100644 index 0000000..9c63e4e --- /dev/null +++ b/src/main/java/com/project/carrot/repository/JPAMemberItemRepository.java @@ -0,0 +1,75 @@ +package com.project.carrot.repository; + +import com.project.carrot.domain.Member; +import com.project.carrot.domain.MemberItem; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityManager; +import java.util.List; +import java.util.Optional; + +public class JPAMemberItemRepository implements MemberItemRepository{ + + private final EntityManager em; + + public JPAMemberItemRepository(EntityManager em) { + this.em = em; + } + + @Transactional + @Override + //cannot reliably process 'persist' call 오류 발생으로 어노테이션 추가 + public MemberItem save(MemberItem memberItem) { + em.persist(memberItem); + return memberItem; + } + + @Override + public List findAll() { + return em.createQuery("select m from MemberItem m",MemberItem.class) + .getResultList(); + } + + @Override + public List findByCategory(String ItemCategory) { + return em.createQuery("select m from MemberItem m where ItemCategory= :ItemCategory" + ,MemberItem.class).setParameter("ItemCategory",ItemCategory).getResultList(); + } + + @Override + public List findByTitle(String ItemTitle) { + return em.createQuery("select m from MemberItem m where ItemTitle= :ItemTitle" + ,MemberItem.class).setParameter("ItemTitle",ItemTitle).getResultList(); + } + + @Override + public List findByContent(String ItemContent) { + return em.createQuery("SELECT m FROM MemberItem m WHERE m.ItemContent LIKE %:ItemContent" + ,MemberItem.class).setParameter("ItemContent",ItemContent).getResultList(); + } + + @Override + public Optional findByItemId(Long ItemId){ + MemberItem memberItem=em.find(MemberItem.class,ItemId); + return Optional.ofNullable(memberItem); + } + + @Override + public Optional findByUserId(Long UserId){ + Member member=em.find(Member.class,UserId); + return Optional.ofNullable(member); + } + + @Override + public List findAllByUserId(Long UserId){ + return em.createQuery("select m from MemberItem m where UserId= :UserId" + ,MemberItem.class).setParameter("UserId",UserId).getResultList(); + } + @Override + public List findAllByBuyUserId(Long BuyUserId){ + return em.createQuery("select m from MemberItem m where BuyUserId= :BuyUserId" + ,MemberItem.class).setParameter("BuyUserId",BuyUserId).getResultList(); + } +} diff --git a/src/main/java/com/project/carrot/repository/JPAMemberRepository.java b/src/main/java/com/project/carrot/repository/JPAMemberRepository.java new file mode 100644 index 0000000..c491bf2 --- /dev/null +++ b/src/main/java/com/project/carrot/repository/JPAMemberRepository.java @@ -0,0 +1,57 @@ +package com.project.carrot.repository; + +import com.project.carrot.domain.Member; + +import javax.persistence.EntityManager; +import java.util.List; +import java.util.Optional; + +public class JPAMemberRepository implements MemberRepository{ + private final EntityManager em; + + public JPAMemberRepository(EntityManager em) { + this.em = em; + } + + @Override + public Member save(Member member) { + em.persist(member); + return member; + } + @Override + public Optional findById(Long Userid){ + Member member=em.find(Member.class,Userid); + return Optional.ofNullable(member); + } + @Override + public Optional findByEmail(String UserEmail) { + List result = em.createQuery("select m from Member m where UserEmail= :UserEmail" + , Member.class).setParameter("UserEmail",UserEmail).getResultList(); + return result.stream().findAny(); + } + + @Override + public Optional findByName(String UserName) { + List result = em.createQuery("select m from Member m where UserName= :UserName" + , Member.class).setParameter("UserName",UserName).getResultList(); + return result.stream().findAny(); + } + + @Override + public Optional findByPass(String UserPass) { + List result = em.createQuery("select m from Member m where UserPass= :UserPass" + , Member.class).setParameter("UserPass",UserPass).getResultList(); + + return result.stream().findAny(); + } + + @Override + public Optional findByUserTel(String UserTel) { + List result = em.createQuery("select m from Member m where UserTel= :UserTel" + , Member.class).setParameter("UserTel",UserTel).getResultList(); + + return result.stream().findAny(); + } + + +} diff --git a/src/main/java/com/project/carrot/repository/MemberItemRepository.java b/src/main/java/com/project/carrot/repository/MemberItemRepository.java new file mode 100644 index 0000000..6ec42e9 --- /dev/null +++ b/src/main/java/com/project/carrot/repository/MemberItemRepository.java @@ -0,0 +1,23 @@ +package com.project.carrot.repository; + +import com.project.carrot.domain.Member; +import com.project.carrot.domain.MemberItem; + +import java.util.List; +import java.util.Optional; + +public interface MemberItemRepository { + MemberItem save(MemberItem memberItem); + List findAll(); + List findByCategory(String ItemCategory); + List findByTitle(String ItemTitle); + List findByContent(String ItemContent); + + Optional findByItemId(Long ItemId); + Optional findByUserId(Long UserId); + List findAllByUserId(Long UserId); + List findAllByBuyUserId(Long BuyUserId); + + + +} \ No newline at end of file diff --git a/src/main/java/com/project/carrot/repository/MemberRepository.java b/src/main/java/com/project/carrot/repository/MemberRepository.java new file mode 100644 index 0000000..c427b50 --- /dev/null +++ b/src/main/java/com/project/carrot/repository/MemberRepository.java @@ -0,0 +1,16 @@ +package com.project.carrot.repository; + +import com.project.carrot.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +public interface MemberRepository{ + Member save(Member member); + Optional findById(Long Id); + Optional findByEmail(String email); + Optional findByName(String name); + Optional findByPass(String pass); + Optional findByUserTel(String UserTel); +} diff --git a/src/main/java/com/project/carrot/security/JwtAuthenticationFilter.java b/src/main/java/com/project/carrot/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..eb7115f --- /dev/null +++ b/src/main/java/com/project/carrot/security/JwtAuthenticationFilter.java @@ -0,0 +1,39 @@ +package com.project.carrot.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.GenericFilterBean; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends GenericFilterBean { + private final JwtTokenProvider jwtTokenProvider; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + //SecurityFilter에서 넘어오는 Request & Response를 래핑(캐싱) + ContentCachingRequestWrapper wrappingRequest = new ContentCachingRequestWrapper((HttpServletRequest) request); + ContentCachingResponseWrapper wrappingResponse = new ContentCachingResponseWrapper((HttpServletResponse) response); + // 이하는 JWT 로직, 헤더에서 JWT 를 받아옵니다. + String token = jwtTokenProvider.resolveToken((HttpServletRequest) request); + // 유효한 토큰인지 확인합니다. + if (token != null && jwtTokenProvider.validateToken(token)) { + // 토큰이 유효하면 토큰으로부터 유저 정보를 받아옵니다. + Authentication authentication = jwtTokenProvider.getAuthentication(token); + // SecurityContext 에 Authentication 객체를 저장합니다. + SecurityContextHolder.getContext().setAuthentication(authentication); + } + chain.doFilter(wrappingRequest, wrappingResponse); + wrappingResponse.copyBodyToResponse(); //캐싱된 응답값을 덮어씀 + } +} diff --git a/src/main/java/com/project/carrot/security/JwtTokenProvider.java b/src/main/java/com/project/carrot/security/JwtTokenProvider.java new file mode 100644 index 0000000..fef14bc --- /dev/null +++ b/src/main/java/com/project/carrot/security/JwtTokenProvider.java @@ -0,0 +1,80 @@ +package com.project.carrot.security; + +import com.project.carrot.Service.MemberService; +import com.project.carrot.domain.Member; +import com.project.carrot.repository.MemberRepository; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import java.util.Base64; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +@RequiredArgsConstructor +@Component +public class JwtTokenProvider { + private String secretKey = "1myprojectsecret1"; + + // 토큰 유효시간 30분 + private long tokenValidTime = 30 * 60 * 1000L; + + private final UserDetailsService userDetailsService; + + // 객체 초기화, secretKey를 Base64로 인코딩한다. + @PostConstruct + protected void init() { + secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); + } + + // JWT 토큰 생성 + public String createToken(String userPk, List roles) { + Claims claims = Jwts.claims().setSubject(userPk); // JWT payload 에 저장되는 정보단위, 보통 여기서 user를 식별하는 값을 넣는다. + claims.put("roles", roles); // 정보는 key / value 쌍으로 저장된다. + Date now = new Date(); + return Jwts.builder() + .setClaims(claims) // 정보 저장 + .setIssuedAt(now) // 토큰 발행 시간 정보 + .setExpiration(new Date(now.getTime() + tokenValidTime)) // set Expire Time + .signWith(SignatureAlgorithm.HS256, secretKey) // 사용할 암호화 알고리즘과 + // signature 에 들어갈 secret값 세팅 + .compact(); + } + + // JWT 토큰에서 인증 정보 조회 + public Authentication getAuthentication(String token) { + UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUserPk(token)); + return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + } + + // 토큰에서 회원 정보 추출 + public String getUserPk(String token) { + return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); + } + + // Request의 Header에서 token 값을 가져옵니다. "Authorization" : "TOKEN값' + public String resolveToken(HttpServletRequest request) { + return request.getHeader("Authorization"); + } + + // 토큰의 유효성 + 만료일자 확인 + public boolean validateToken(String jwtToken) { + try { + Jws claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken); + return !claims.getBody().getExpiration().before(new Date()); + } catch (Exception e) { + return false; + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..7ceb73a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,22 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/carrot?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul +spring.datasource.username=root +spring.datasource.password=1224 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +# MySQL ? ??? ?. +spring.jpa.database=mysql + + +# MySQL ?? ?? +spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect + +spring.jpa.hibernate.ddl-auto=update + +spring.jpa.show-sql=true + +spring.jpa.generate-ddl=true + +#token secret key + +#spring.main.allow-bean-definition-overriding=true +#bean ?? ?? ???? ???? memberRepository ?? ?? diff --git a/src/test/java/com/dku/springstudy/SpringStudyApplicationTests.java b/src/test/java/com/project/carrot/CarrotApplicationTests.java similarity index 69% rename from src/test/java/com/dku/springstudy/SpringStudyApplicationTests.java rename to src/test/java/com/project/carrot/CarrotApplicationTests.java index 79d9975..753cf97 100644 --- a/src/test/java/com/dku/springstudy/SpringStudyApplicationTests.java +++ b/src/test/java/com/project/carrot/CarrotApplicationTests.java @@ -1,10 +1,10 @@ -package com.dku.springstudy; +package com.project.carrot; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class SpringStudyApplicationTests { +class testCarrotApplicationTests { @Test void contextLoads() { diff --git a/src/test/java/com/project/carrot/MemberRepositorytest.java b/src/test/java/com/project/carrot/MemberRepositorytest.java new file mode 100644 index 0000000..13b5d5d --- /dev/null +++ b/src/test/java/com/project/carrot/MemberRepositorytest.java @@ -0,0 +1,32 @@ +package com.project.carrot; + +import com.project.carrot.domain.Member; +import com.project.carrot.repository.MemberRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional + +public class MemberRepositorytest { + + @Autowired//이거때문에 하 + MemberRepository memberRepository; + @Test + void 회원등록() throws Exception{ + + Member member=new Member(); + member=member.builder() + .UserEmail("t@52") + .UserPass("12241").UserTel("01022-1").UserName("Yun1") + .NickName("Nick1").Address("incheon1").build(); + + memberRepository.save(member); + + Assertions.assertThat(member.getUserName()).isEqualTo("Yun1"); + + } +} diff --git a/src/test/java/com/project/carrot/MemberServiceTest.java b/src/test/java/com/project/carrot/MemberServiceTest.java new file mode 100644 index 0000000..ae6392f --- /dev/null +++ b/src/test/java/com/project/carrot/MemberServiceTest.java @@ -0,0 +1,75 @@ +package com.project.carrot; + +import com.project.carrot.domain.Member; +import com.project.carrot.Service.MemberService; +import com.project.carrot.repository.MemberRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +public class MemberServiceTest { + @Autowired + MemberService memberService; + @Autowired + MemberRepository memberRepository; + + @Test + void 회원가입() throws Exception{ + Member member=new Member(); + member=member.builder() + .UserEmail("t@5") + .UserPass("1224").UserTel("01022-2").UserName("Yun") + .NickName("Nick").Address("incheon").build(); + + memberService.join(member); + + //Assertions.assertThat(id).isEqualTo(member.getUserId()); + + System.out.println(member); + } + + @Test + void 중복체크() throws Exception{ + Member member=new Member(); + member=member.builder() + .UserEmail("t@5") + .UserPass("1224").UserTel("01022-2").UserName("Yun") + .NickName("Nick").Address("incheon").build(); + + memberService.join(member); + + var ref = new Object() { + Member member2 = new Member(); + }; + ref.member2 = ref.member2.builder() + .UserEmail("t@5") + .UserPass("1224").UserTel("01022-2").UserName("Yun") + .NickName("Nick").Address("incheon").build(); + + IllegalStateException e = org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, + ()->memberService.join(ref.member2)); + + + Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 이메일입니다."); + } + + @Test + void 로그인() throws Exception{ + Member member=new Member(); + member=member.builder() + .UserEmail("t@5") + .UserPass("1224").UserTel("01022-2").UserName("Yun") + .NickName("Nick").Address("incheon").build(); + + memberService.join(member); + + IllegalStateException e= org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, + ()->memberService.Login("t@56","1225")); + + Assertions.assertThat(e.getMessage()).isEqualTo("이메일이 일치하지 않습니다."); + } +}