-
냉장고를 부탁해!Studying/Proj 과정 2022. 10. 3. 22:15
Github
https://github.com/Kim-DaHam/SWEDU_2022_RecipeApp_FrontEnd
실행 화면
1. 분석단계 - 요구사항명세서
2022-10-03-MON 에 최종 승인 완료
2. 설계단계 - 설계명세서
『2. 개발 및 운영환경』 작성 중
Framwork 부분 참고
(J2EE, EJB, Spring): https://choichumji.tistory.com/133
(JSP, Servelt): https://m.blog.naver.com/acornedu/221128616501
3. 구현 단계 - 백엔드
회원관리(S1-B01)
회원가입 (S1-P11)
UserRepository.java
더보기package com.project.recipeapp.persistence; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.project.recipeapp.model.UserEntity; @Repository public interface UserRepository extends JpaRepository<UserEntity, String>{ UserEntity findByMemail(String email); Boolean existsByMemail(String email); UserEntity findByMemailAndMpw(String email, String password); }
UserEntity.java
더보기package com.project.recipeapp.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import org.hibernate.annotations.GenericGenerator; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @Entity @Builder @NoArgsConstructor @AllArgsConstructor @Table(name="T_MEMBER", uniqueConstraints = {@UniqueConstraint(columnNames = "M_EMAIL")}) public class UserEntity { @Id @GeneratedValue(generator="system-uuid") @GenericGenerator(name="system-uuid",strategy="uuid") @Column(name="M_KEY", nullable=false) private String mkey; @Column(name="M_NAME", nullable=false) private String mname; @Column(name="M_EMAIL", nullable=false) private String memail; @Column(name="M_PW", nullable=false) private String mpw; }
UserDTO.java
더보기package com.project.recipeapp.dto; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class UserDTO { private String token; private String memail; private String mname; private String mpw; private String mkey; }
ResponseDTO.java
더보기package com.project.recipeapp.dto; import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data public class ResponseDTO<T>{ private String error; private List<T> data; }
로그인/로그아웃 (S1-P14)
UserService.java
더보기package com.project.recipeapp.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.project.recipeapp.model.UserEntity; import com.project.recipeapp.persistence.UserRepository; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class UserService { @Autowired private UserRepository userRepository; public UserEntity create(final UserEntity userEntity) { if(userEntity == null || userEntity.getMemail() == null) { throw new RuntimeException("Invalid arguments"); } final String email = userEntity.getMemail(); if(userRepository.existsByMemail(email)) { log.warn("Email already exists {}",email); throw new RuntimeException("Email already exists"); } return userRepository.save(userEntity); } public UserEntity getByCredentials(final String email, final String password) { return userRepository.findByMemailAndMpw(email, password); } }
UserController.java
더보기package com.project.recipeapp.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.project.recipeapp.dto.ResponseDTO; import com.project.recipeapp.dto.UserDTO; import com.project.recipeapp.model.UserEntity; import com.project.recipeapp.security.TokenProvider; import com.project.recipeapp.service.UserService; import lombok.extern.slf4j.Slf4j; @CrossOrigin(origins = "*") @Slf4j @RestController @RequestMapping("/auth") public class UserController { @Autowired private UserService userService; @Autowired private TokenProvider tokenProvider; @PostMapping("/signup") public ResponseEntity<?>registerUser(@RequestBody UserDTO userDTO){ try { UserEntity user = UserEntity.builder() .memail(userDTO.getMemail()) .mname(userDTO.getMname()) .mpw(userDTO.getMpw()) .build(); UserEntity registeredUser = userService.create(user); UserDTO responseUserDTO = userDTO.builder() .memail(registeredUser.getMemail()) .mkey(registeredUser.getMkey()) .mname(registeredUser.getMname()) .build(); return ResponseEntity.ok().body(responseUserDTO); }catch(Exception e){ ResponseDTO responseDTO = ResponseDTO.builder().error(e.getMessage()).build(); return ResponseEntity.badRequest().body(responseDTO); } } @PostMapping("/signin") public ResponseEntity<?>authenticate(@RequestBody UserDTO userDTO){ UserEntity user = userService.getByCredentials(userDTO.getMemail(),userDTO.getMpw()); if(user !=null){ final String token = tokenProvider.create(user); final UserDTO responseUserDTO = UserDTO.builder() .memail(user.getMemail()) .mkey(user.getMkey()) .token(token) .build(); return ResponseEntity.ok().body(responseUserDTO); }else { ResponseDTO responseDTO = ResponseDTO.builder() .error("Login failed") .build(); return ResponseEntity.badRequest().body(responseDTO); } } }
JwtAuthenticationFilter.java
더보기package com.project.recipeapp.security; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.util.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import lombok.extern.slf4j.Slf4j; @Slf4j @Component public class JwtAuthenticationFilter extends OncePerRequestFilter{ @Autowired private TokenProvider tokenProvider; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String token =parseBearerToken(request); log.info("Filter is running..."); if(token !=null &&!token.equalsIgnoreCase("null")){ String userId = tokenProvider.validateAndGetUserId(token); log.info("Authenticated user ID : "+ userId); AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userId,null,AuthorityUtils.NO_AUTHORITIES); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); } }catch(Exception ex){ logger.error("Could not set user authentication in security context",ex); } filterChain.doFilter(request, response); } private String parseBearerToken(HttpServletRequest request){ String bearerToken = request.getHeader("Authorization"); if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")){ return bearerToken.substring(7); } return null; } }
WebSecurityConfig.java
더보기package com.project.recipeapp.config; import org.springframework.web.filter.CorsFilter; import com.project.recipeapp.security.JwtAuthenticationFilter; import org.springframework.beans.factory.annotation.Autowired; 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 lombok.extern.slf4j.Slf4j; @EnableWebSecurity @Slf4j public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationFilter jwtAuthenticationFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.cors() .and() .csrf() .disable() .httpBasic() .disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/","/auth/**").permitAll() .anyRequest() .authenticated(); http.addFilterAfter(jwtAuthenticationFilter, CorsFilter.class); } }
FridgeController.java
더보기package com.project.recipeapp.controller; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.project.recipeapp.dto.FridgeDTO; import com.project.recipeapp.dto.FridgeResponseDTO; import com.project.recipeapp.model.FridgeEntity; import com.project.recipeapp.service.FridgeService; import lombok.extern.slf4j.Slf4j; @Slf4j @CrossOrigin(origins="*") @RestController @RequestMapping("fridge") public class FridgeController { @Autowired private FridgeService service; @PostMapping public ResponseEntity<?> createGrocery(@AuthenticationPrincipal String userId, @RequestBody FridgeDTO dto){ try { FridgeEntity entity = FridgeDTO.toEntity(dto); entity.setMember(userId); entity.setDate(LocalDate.now()); List<FridgeEntity> entities = service.create(entity); List<FridgeDTO> dtos = entities.stream().map(FridgeDTO::new) .collect(Collectors.toList()); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); } catch(Exception e) { String error = e.getMessage(); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } @GetMapping public ResponseEntity<?> retrieveGroceryList(@AuthenticationPrincipal String userId){ List<FridgeEntity> entities = service.retrieve(userId); List<FridgeDTO> dtos = entities.stream().map(FridgeDTO::new) .collect(Collectors.toList()); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); } @PutMapping public ResponseEntity<?> updateGrocery(@AuthenticationPrincipal String userId, @RequestBody FridgeDTO dto){ try { FridgeEntity entity = FridgeDTO.toEntity(dto); entity.setMember(userId); List<FridgeEntity> entities = service.update(entity); List<FridgeDTO> dtos = entities.stream().map(FridgeDTO::new) .collect(Collectors.toList()); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } @DeleteMapping public ResponseEntity<?> deleteGrocery(@AuthenticationPrincipal String userId, @RequestBody List<FridgeDTO> dto){ try { List<FridgeEntity> deleteList = new ArrayList(); dto.forEach((d)->{ if(dto.size()==1 || d.isChecked()) deleteList.add(FridgeDTO.toEntity(d)); }); List<FridgeEntity> entities = service.delete(deleteList, userId); List<FridgeDTO> dtos = entities.stream().map(FridgeDTO::new) .collect(Collectors.toList()); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } }
FridgeService.java
더보기package com.project.recipeapp.service; import java.util.List; import java.util.Optional; import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.project.recipeapp.model.FridgeEntity; import com.project.recipeapp.persistence.FridgeRepository; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FridgeService { @Autowired private FridgeRepository repository; public List<FridgeEntity> create(final FridgeEntity entity){ validate(entity); repository.save(entity); return repository.findByMember(entity.getMember()); } public List<FridgeEntity> retrieve(final String member){ return repository.findByMember(member); } public List<FridgeEntity> update(final FridgeEntity entity) { validate(entity); final Optional<FridgeEntity> original = repository.findByKey(entity.getKey()); original.ifPresent(grocery -> { grocery.setName(entity.getName()); grocery.setCategory(entity.getCategory()); grocery.setChecked(entity.isChecked()); repository.save(grocery); }); return repository.findByMember(entity.getMember()); } @Transactional public List<FridgeEntity> delete(final List<FridgeEntity> entities, final String member){ entities.forEach((entity)->{ if(repository.existsByKey(entity.getKey())) repository.deleteByKey(entity.getKey()); else throw new RuntimeException("key does not exist"); }); return repository.findByMember(member); } public void validate(final FridgeEntity entity) { if(entity==null) { log.warn("Entity cannot be null"); throw new RuntimeException("Entity cannot be null."); } if(entity.getMember()==null) { log.warn("Unknown user."); throw new RuntimeException("Unknown user."); } } }
RecipeController.java
더보기package com.project.recipeapp.controller; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.project.recipeapp.dto.FridgeDTO; import com.project.recipeapp.dto.FridgeResponseDTO; import com.project.recipeapp.dto.IngredientDTO; import com.project.recipeapp.dto.IngredientResponseDTO; import com.project.recipeapp.dto.RecipeDTO; import com.project.recipeapp.dto.RecipeResponseDTO; import com.project.recipeapp.model.IngredientEntity; import com.project.recipeapp.model.RecipeEntity; import com.project.recipeapp.service.RecipeService; import lombok.extern.slf4j.Slf4j; @Slf4j @CrossOrigin(origins="*") @RestController @RequestMapping("recipe") public class RecipeController { @Autowired private RecipeService service; @PostMapping public ResponseEntity<?> createRecipe(@AuthenticationPrincipal String userId, @RequestBody RecipeDTO dto){ try { RecipeEntity entity = RecipeDTO.toEntity(dto); entity.setRmember(userId); entity.setRdate(LocalDate.now()); List<RecipeEntity> entities = service.Rcreate(entity); List<RecipeDTO> dtos = entities.stream().map(RecipeDTO::new) .collect(Collectors.toList()); RecipeResponseDTO<RecipeDTO> response = RecipeResponseDTO .<RecipeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); RecipeResponseDTO<RecipeDTO> response = RecipeResponseDTO .<RecipeDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } @PostMapping("/ingredient") public ResponseEntity<?> createIngredient(@AuthenticationPrincipal String userId, @RequestBody IngredientDTO dto){ try { IngredientEntity entity = IngredientDTO.toEntity(dto); entity.setRkey(dto.getRkey()); List<IngredientEntity> entities = service.Icreate(entity); List<IngredientDTO> dtos = entities.stream().map(IngredientDTO::new) .collect(Collectors.toList()); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } @GetMapping public ResponseEntity<?> retrieveRecipe(@AuthenticationPrincipal String userId, @RequestBody Map<String, String> cate){ String category = cate.get("category"); List<RecipeEntity> entities = service.Rretrieve(userId, category); List<RecipeDTO> dtos = entities.stream().map(RecipeDTO::new) .collect(Collectors.toList()); RecipeResponseDTO<RecipeDTO> response = RecipeResponseDTO .<RecipeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); } @GetMapping("/ingredient") public ResponseEntity<?> retrieveIngredient(@AuthenticationPrincipal String userId, @RequestBody Map<String, String> recipeKey){ String rkey = recipeKey.get("rkey"); List<IngredientEntity> entities = service.Iretrieve(rkey); List<IngredientDTO> dtos = entities.stream().map(IngredientDTO::new) .collect(Collectors.toList()); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); } @DeleteMapping("/ingredient") public ResponseEntity<?> deleteIngredient(@AuthenticationPrincipal String userId, @RequestBody IngredientDTO dto){ try { IngredientEntity entity = IngredientDTO.toEntity(dto); List<IngredientEntity> entities = service.Idelete(entity); List<IngredientDTO> dtos = entities.stream().map(IngredientDTO::new) .collect(Collectors.toList()); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } }
RecipeService.java
더보기package com.project.recipeapp.service; import java.util.ArrayList; import java.util.List; import java.util.Optional; import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.project.recipeapp.model.FridgeEntity; import com.project.recipeapp.model.IngredientEntity; import com.project.recipeapp.model.RecipeEntity; import com.project.recipeapp.persistence.IngredientRepository; import com.project.recipeapp.persistence.RecipeRepository; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class RecipeService { @Autowired private RecipeRepository Rrepository; @Autowired private IngredientRepository Irepository; public List<RecipeEntity> Rcreate(final RecipeEntity entity){ List<RecipeEntity> Rlist = new ArrayList(); validate(entity); Rrepository.save(entity); Rlist.addAll(Rrepository.findByRmember("admin")); Rlist.addAll(Rrepository.findByRmember(entity.getRmember())); return Rlist; } public List<IngredientEntity> Icreate(final IngredientEntity entity){ Irepository.save(entity); return Irepository.findByRkey(entity.getRkey()); } public List<RecipeEntity> Rretrieve(final String member, String category){ List<RecipeEntity> Rlist = new ArrayList(); String admin = "admin"; if(category==null) { Rlist.addAll(Rrepository.findByRmember(admin)); Rlist.addAll(Rrepository.findByRmember(member)); } else { switch(category) { case "아침": Rlist.addAll(Rrepository.findByRmemberAndRcategory(admin, "아침")); Rlist.addAll(Rrepository.findByRmemberAndRcategory(member, "아침")); break; case "점심": Rlist.addAll(Rrepository.findByRmemberAndRcategory(admin, "점심")); Rlist.addAll(Rrepository.findByRmemberAndRcategory(member, "점심")); break; case "저녁": Rlist.addAll(Rrepository.findByRmemberAndRcategory(admin, "저녁")); Rlist.addAll(Rrepository.findByRmemberAndRcategory(member, "저녁")); break; } } return Rlist; } public List<IngredientEntity> Iretrieve(final String rkey){ List<IngredientEntity> Ilist = new ArrayList(); Ilist.addAll(Irepository.findByRkey(rkey)); return Ilist; } @Transactional public List<IngredientEntity> Idelete(final IngredientEntity entity){ if(Irepository.existsByIkey(entity.getIkey())) Irepository.deleteByIkey(entity.getIkey()); else throw new RuntimeException("key does not exist"); return Irepository.findByRkey(entity.getRkey()); } public void validate(final RecipeEntity entity) { if(entity==null) { log.warn("Entity cannot be null"); throw new RuntimeException("Entity cannot be null."); } if(entity.getRmember()==null) { log.warn("Unknown user."); throw new RuntimeException("Unknown user."); } } }
식재료 관리(S1-B02)
식재료 확인(S1-P21)
RecipeappApplication.java
더보기package com.project.recipeapp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RecipeappApplication { public static void main(String[] args) { SpringApplication.run(RecipeappApplication.class, args); } }
FridgeRepository.java
더보기package com.project.recipeapp.persistence; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.project.recipeapp.model.FridgeEntity; @Repository public interface FridgeRepository extends JpaRepository<FridgeEntity, String>{ Optional<FridgeEntity> findByKey(String key); List<FridgeEntity> findByMember(String member); boolean existsByKey(String key); Optional<FridgeEntity> deleteByKey(String key); }
FridgeEntity.java
더보기package com.project.recipeapp.model; import java.time.LocalDate; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data @Entity @Table(name="T_GROCERY") public class FridgeEntity { @Id @GeneratedValue(generator="system-uuid") @GenericGenerator(name="system-uuid", strategy="uuid") @Column(name="G_KEY", nullable=false) private String key; @Column(name="G_NAME", nullable=false, length=50) private String name; @Column(name="G_CATEGORY", length=20) private String category; @Column(name="G_CHECK", nullable=false) private boolean checked; @Column(name="G_EXDATE") private LocalDate exdate; @Column(name="G_DATE", nullable=false) private LocalDate date; //@JoinColumn(name="R_MEMBER", nullable=false) //private MemberEntity mkey; @Column(name="G_MEMBER", nullable=false) private String member; }
-참고-
더보기- 스프링부트 Entity 테이블 Date 타입 - https://jojoldu.tistory.com/527
- 스프링부트 LocalDate, LocalTime, LocalDateTime - https://footprint-of-nawin.tistory.com/67
- 뭘 잘못 건들여서... FridgeEntity.java를 못 찾는다 그런 식의 에러가 뜸 - 프로젝트 우클릭>Java Build Path>Libraries>Classpath에 FridgeEntity 뭐가 있길래 Remove > 다시 실행 > 성공
- 외래키 Join - https://siyoon210.tistory.com/27
- -
FridgeDTO.java
더보기package com.project.recipeapp.dto; import java.time.LocalDate; import com.project.recipeapp.model.FridgeEntity; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data public class FridgeDTO { private String key; private String name; private String category; private boolean checked; private LocalDate exdate; private String member; public FridgeDTO(final FridgeEntity entity) { this.key = entity.getKey(); this.name = entity.getName(); this.category = entity.getCategory(); this.checked = entity.isChecked(); this.exdate = entity.getExdate(); this.member = entity.getMember(); } public static FridgeEntity toEntity(final FridgeDTO dto) { return FridgeEntity.builder() .key(dto.getKey()) .name(dto.getName()) .category(dto.getCategory()) .checked(dto.isChecked()) .exdate(dto.getExdate()) .member(dto.getMember()) .build(); } }
-참고-
더보기- JAVA final 키워드 - https://codechacha.com/ko/java-final-keyword/ https://sabarada.tistory.com/148
- final과 static의 차이 - https://gobae.tistory.com/3
- -
FridgeResponseDTO.java
더보기package com.project.recipeapp.dto; import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data public class FridgeResponseDTO<T> { private String error; private List<T> data; }
-참고-
더보기- java 제네릭(Generic)타입 - https://st-lab.tistory.com/153
- -
FridgeService.java
더보기package com.project.recipeapp.service; import java.util.List; import java.util.Optional; import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.project.recipeapp.model.FridgeEntity; import com.project.recipeapp.persistence.FridgeRepository; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FridgeService { @Autowired private FridgeRepository repository; public List<FridgeEntity> create(final FridgeEntity entity){ validate(entity); repository.save(entity); return repository.findByMember(entity.getMember()); } public List<FridgeEntity> retrieve(final String member){ return repository.findByMember(member); } public List<FridgeEntity> update(final FridgeEntity entity) { validate(entity); final Optional<FridgeEntity> original = repository.findByKey(entity.getKey()); original.ifPresent(grocery -> { grocery.setName(entity.getName()); grocery.setCategory(entity.getCategory()); grocery.setChecked(entity.isChecked()); repository.save(grocery); }); return repository.findByMember(entity.getMember()); } @Transactional public List<FridgeEntity> delete(final List<FridgeEntity> entities, final String member){ entities.forEach((entity)->{ if(repository.existsByKey(entity.getKey())) repository.deleteByKey(entity.getKey()); else throw new RuntimeException("key does not exist"); }); return repository.findByMember(member); } public void validate(final FridgeEntity entity) { if(entity==null) { log.warn("Entity cannot be null"); throw new RuntimeException("Entity cannot be null."); } if(entity.getMember()==null) { log.warn("Unknown user."); throw new RuntimeException("Unknown user."); } } }
-참고-
더보기- java Optional - https://mangkyu.tistory.com/70
- JPA findBy~ 메소드 - https://devfunny.tistory.com/426 https://exhibitlove.tistory.com/262
- delete 기능 에러(cannot reliably process 'remove' call) - https://csy7792.tistory.com/134
- -
FridgeController.java
더보기package com.project.recipeapp.controller; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.project.recipeapp.dto.FridgeDTO; import com.project.recipeapp.dto.FridgeResponseDTO; import com.project.recipeapp.model.FridgeEntity; import com.project.recipeapp.service.FridgeService; import lombok.extern.slf4j.Slf4j; @Slf4j @CrossOrigin(origins="*") @RestController @RequestMapping("fridge") public class FridgeController { @Autowired private FridgeService service; @PostMapping public ResponseEntity<?> createGrocery(@AuthenticationPrincipal String userId, @RequestBody FridgeDTO dto){ try { FridgeEntity entity = FridgeDTO.toEntity(dto); entity.setMember(userId); entity.setDate(LocalDate.now()); List<FridgeEntity> entities = service.create(entity); List<FridgeDTO> dtos = entities.stream().map(FridgeDTO::new) .collect(Collectors.toList()); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); } catch(Exception e) { String error = e.getMessage(); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } @GetMapping public ResponseEntity<?> retrieveGroceryList(@AuthenticationPrincipal String userId){ List<FridgeEntity> entities = service.retrieve(userId); List<FridgeDTO> dtos = entities.stream().map(FridgeDTO::new) .collect(Collectors.toList()); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); } @PutMapping public ResponseEntity<?> updateGrocery(@AuthenticationPrincipal String userId, @RequestBody FridgeDTO dto){ try { FridgeEntity entity = FridgeDTO.toEntity(dto); entity.setMember(userId); List<FridgeEntity> entities = service.update(entity); List<FridgeDTO> dtos = entities.stream().map(FridgeDTO::new) .collect(Collectors.toList()); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } @DeleteMapping public ResponseEntity<?> deleteGrocery(@AuthenticationPrincipal String userId, @RequestBody List<FridgeDTO> dto){ try { List<FridgeEntity> deleteList = new ArrayList(); dto.forEach((d)->{ if(dto.size()==1 || d.isChecked()) deleteList.add(FridgeDTO.toEntity(d)); }); List<FridgeEntity> entities = service.delete(deleteList, userId); List<FridgeDTO> dtos = entities.stream().map(FridgeDTO::new) .collect(Collectors.toList()); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); FridgeResponseDTO<FridgeDTO> response = FridgeResponseDTO .<FridgeDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } }
-참고-
더보기- 현재시간 구하기 - https://hianna.tistory.com/607
- -
레시피 관리(S1-B03)
레시피 확인(S1-P31)
RecipeRepository.java
더보기package com.project.recipeapp.persistence; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.project.recipeapp.model.IngredientEntity; import com.project.recipeapp.model.RecipeEntity; @Repository public interface RecipeRepository extends JpaRepository<RecipeEntity, String>{ List<RecipeEntity> findByRmember(String member); List<RecipeEntity> findByRmemberAndRcategory(String member, String categorty); }
IngredientRepository.java
더보기package com.project.recipeapp.persistence; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.project.recipeapp.model.IngredientEntity; @Repository public interface IngredientRepository extends JpaRepository<IngredientEntity, String>{ Optional<IngredientEntity> findByIkey(String key); List<IngredientEntity> findByRkey(String key); List<IngredientEntity> deleteByIkey(String key); boolean existsByIkey(String key); }
RecipeEntity.java
더보기package com.project.recipeapp.model; import java.time.LocalDate; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data @Entity @Table(name="T_RECIPE") public class RecipeEntity { @Id @GeneratedValue(generator="system-uuid") @GenericGenerator(name="system-uuid", strategy="uuid") @Column(name="R_KEY", nullable=false) private String rkey; @Column(name="R_NAME", nullable=false, length=50) private String rname; @Column(name="R_CONTENT", nullable=false, length=5000) private String rcontent; @Column(name="R_CATEGORY", length=10) private String rcategory; @Column(name="R_IMAGE", length=50) private String rimage; @Column(name="R_DATE", nullable=false) private LocalDate rdate; //@JoinColumn(name="R_MEMBER", nullable=false) //private MemberEntity mkey; @Column(name="R_MEMBER", nullable=false) private String rmember; }
-참고-
더보기- 외래키 - https://brunch.co.kr/@dan-kim/26
- 자바 패키지가 폴더로 변했을 때 - https://flea.tistory.com/7
- JPA 엔티티 어노테이션 - https://cjw-awdsd.tistory.com/46
- -
IngredientEntity.java
더보기package com.project.recipeapp.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data @Entity @Table(name="T_INGREDIENT") public class IngredientEntity { @Id @GeneratedValue(generator="system-uuid") @GenericGenerator(name="system-uuid", strategy="uuid") @Column(name="I_KEY", nullable=false) private String ikey; @Column(name="I_INGREDIENT", nullable=false, length=50) private String ingredient; //@JoinColumn(name="R_KEY", nullable=false) //private RecipeEntity rkey; @Column(name="R_KEY", nullable=false) private String rkey; }
RecipeDTO.java
더보기package com.project.recipeapp.dto; import com.project.recipeapp.model.RecipeEntity; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data public class RecipeDTO { private String rkey; private String rname; private String rcontent; private String rcategory; private String rimage; public RecipeDTO(final RecipeEntity entity) { this.rkey = entity.getRkey(); this.rname = entity.getRname(); this.rcontent = entity.getRcontent(); this.rcategory = entity.getRcategory(); this.rimage = entity.getRimage(); } public static RecipeEntity toEntity(final RecipeDTO dto) { return RecipeEntity.builder() .rkey(dto.getRkey()) .rname(dto.getRname()) .rcontent(dto.getRcontent()) .rcategory(dto.getRcategory()) .rimage(dto.getRimage()) .build(); } }
RecipeResponseDTO.java
더보기package com.project.recipeapp.dto; import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data public class RecipeResponseDTO<T> { private String error; private List<T> data; }
IngredientDTO.java
더보기package com.project.recipeapp.dto; import com.project.recipeapp.model.IngredientEntity; import com.project.recipeapp.model.RecipeEntity; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor @Data public class IngredientDTO { private String ikey; private String ingredient; //private RecipeEntity rkey; private String rkey; public IngredientDTO(final IngredientEntity entity) { this.ikey = entity.getIkey(); this.ingredient = entity.getIngredient(); this.rkey = entity.getRkey(); } public static IngredientEntity toEntity(final IngredientDTO dto) { return IngredientEntity.builder() .ikey(dto.getIkey()) .ingredient(dto.getIngredient()) .rkey(dto.getRkey()) .build(); } }
RecipeService.java
더보기package com.project.recipeapp.service; import java.util.ArrayList; import java.util.List; import java.util.Optional; import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.project.recipeapp.model.FridgeEntity; import com.project.recipeapp.model.IngredientEntity; import com.project.recipeapp.model.RecipeEntity; import com.project.recipeapp.persistence.IngredientRepository; import com.project.recipeapp.persistence.RecipeRepository; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class RecipeService { @Autowired private RecipeRepository Rrepository; @Autowired private IngredientRepository Irepository; public List<RecipeEntity> Rcreate(final RecipeEntity entity){ List<RecipeEntity> Rlist = new ArrayList(); validate(entity); Rrepository.save(entity); Rlist.addAll(Rrepository.findByRmember("admin")); Rlist.addAll(Rrepository.findByRmember(entity.getRmember())); return Rlist; } public List<IngredientEntity> Icreate(final IngredientEntity entity){ Irepository.save(entity); return Irepository.findByRkey(entity.getRkey()); } public List<RecipeEntity> Rretrieve(final String member, String category){ List<RecipeEntity> Rlist = new ArrayList(); String admin = "admin"; if(category==null) { Rlist.addAll(Rrepository.findByRmember(admin)); Rlist.addAll(Rrepository.findByRmember(member)); } else { switch(category) { case "아침": Rlist.addAll(Rrepository.findByRmemberAndRcategory(admin, "아침")); Rlist.addAll(Rrepository.findByRmemberAndRcategory(member, "아침")); break; case "점심": Rlist.addAll(Rrepository.findByRmemberAndRcategory(admin, "점심")); Rlist.addAll(Rrepository.findByRmemberAndRcategory(member, "점심")); break; case "저녁": Rlist.addAll(Rrepository.findByRmemberAndRcategory(admin, "저녁")); Rlist.addAll(Rrepository.findByRmemberAndRcategory(member, "저녁")); break; } } return Rlist; } public List<IngredientEntity> Iretrieve(final String rkey){ List<IngredientEntity> Ilist = new ArrayList(); Ilist.addAll(Irepository.findByRkey(rkey)); return Ilist; } @Transactional public List<IngredientEntity> Idelete(final IngredientEntity entity){ if(Irepository.existsByIkey(entity.getIkey())) Irepository.deleteByIkey(entity.getIkey()); else throw new RuntimeException("key does not exist"); return Irepository.findByRkey(entity.getRkey()); } public void validate(final RecipeEntity entity) { if(entity==null) { log.warn("Entity cannot be null"); throw new RuntimeException("Entity cannot be null."); } if(entity.getRmember()==null) { log.warn("Unknown user."); throw new RuntimeException("Unknown user."); } } }
-참고-
더보기- java List add/remove 함수 - http://daplus.net/java-%EC%9E%90%EB%B0%94-%ED%95%98%EC%9A%B0%ED%88%AC-arraylist-%ED%91%B8%EC%8B%9C-%ED%8C%9D-%EC%8B%9C%ED%94%84%ED%8A%B8-%EB%B0%8F-%EC%8B%9C%ED%94%84%ED%8A%B8-%ED%95%B4%EC%A0%9C/
- java List 두 개의 리스트 이어붙이기 - https://codechacha.com/ko/java-join-two-lists/
- java forEach() - https://www.tutorialkart.com/java/java-arraylist-foreach/
RecipeController.java
더보기package com.project.recipeapp.controller; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.project.recipeapp.dto.FridgeDTO; import com.project.recipeapp.dto.FridgeResponseDTO; import com.project.recipeapp.dto.IngredientDTO; import com.project.recipeapp.dto.IngredientResponseDTO; import com.project.recipeapp.dto.RecipeDTO; import com.project.recipeapp.dto.RecipeResponseDTO; import com.project.recipeapp.model.IngredientEntity; import com.project.recipeapp.model.RecipeEntity; import com.project.recipeapp.service.RecipeService; import lombok.extern.slf4j.Slf4j; @Slf4j @CrossOrigin(origins="*") @RestController @RequestMapping("recipe") public class RecipeController { @Autowired private RecipeService service; @PostMapping public ResponseEntity<?> createRecipe(@AuthenticationPrincipal String userId, @RequestBody RecipeDTO dto){ try { RecipeEntity entity = RecipeDTO.toEntity(dto); entity.setRmember(userId); entity.setRdate(LocalDate.now()); List<RecipeEntity> entities = service.Rcreate(entity); List<RecipeDTO> dtos = entities.stream().map(RecipeDTO::new) .collect(Collectors.toList()); RecipeResponseDTO<RecipeDTO> response = RecipeResponseDTO .<RecipeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); RecipeResponseDTO<RecipeDTO> response = RecipeResponseDTO .<RecipeDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } @PostMapping("/ingredient") public ResponseEntity<?> createIngredient(@AuthenticationPrincipal String userId, @RequestBody IngredientDTO dto){ try { IngredientEntity entity = IngredientDTO.toEntity(dto); entity.setRkey(dto.getRkey()); List<IngredientEntity> entities = service.Icreate(entity); List<IngredientDTO> dtos = entities.stream().map(IngredientDTO::new) .collect(Collectors.toList()); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } @GetMapping public ResponseEntity<?> retrieveRecipe(@AuthenticationPrincipal String userId, @RequestBody Map<String, String> cate){ String category = cate.get("category"); List<RecipeEntity> entities = service.Rretrieve(userId, category); List<RecipeDTO> dtos = entities.stream().map(RecipeDTO::new) .collect(Collectors.toList()); RecipeResponseDTO<RecipeDTO> response = RecipeResponseDTO .<RecipeDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); } @GetMapping("/ingredient") public ResponseEntity<?> retrieveIngredient(@AuthenticationPrincipal String userId, @RequestBody Map<String, String> recipeKey){ String rkey = recipeKey.get("rkey"); List<IngredientEntity> entities = service.Iretrieve(rkey); List<IngredientDTO> dtos = entities.stream().map(IngredientDTO::new) .collect(Collectors.toList()); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); } @DeleteMapping("/ingredient") public ResponseEntity<?> deleteIngredient(@AuthenticationPrincipal String userId, @RequestBody IngredientDTO dto){ try { IngredientEntity entity = IngredientDTO.toEntity(dto); List<IngredientEntity> entities = service.Idelete(entity); List<IngredientDTO> dtos = entities.stream().map(IngredientDTO::new) .collect(Collectors.toList()); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().data(dtos).build(); return ResponseEntity.ok().body(response); }catch(Exception e) { String error = e.getMessage(); IngredientResponseDTO<IngredientDTO> response = IngredientResponseDTO .<IngredientDTO>builder().error(error).build(); return ResponseEntity.badRequest().body(response); } } }
-참고-
4. 구현 단계 - 프론트엔드
작업 폴더 경로 C:\workspace3\2. recipe project
-참고-
더보기- git 화살표 파일 - https://zzang9ha.tistory.com/346
- -
홈 화면
Home.js
-https://www.w3schools.com/bootstrap4/bootstrap_buttons.asp
-https://www.w3schools.com/w3css/w3css_display.asp
-https://www.w3schools.com/w3css/tryit.asp?filename=tryw3css_templates_coming_soon&stacked=h
-https://pixabay.com/vectors/search/fridge/?manual_search=1
-https://kr.piliapp.com/emoji/google/
※주의사항※
1. w3.css Display에서 middle 속성은 height가 꼭 px로 정해져있어야 한다!!!
-참고-
더보기- css 블러 - https://13akstjq.github.io/scss/2020/11/18/css-filter-%EC%86%8D%EC%84%B1%EC%9C%BC%EB%A1%9C-%EB%B8%94%EB%9F%AC%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0.html
- react-router-dom 설치 - https://velog.io/@jungsw586/React-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0-2.-React-Router-%EC%84%A4%EC%B9%98
- css background-img - https://superkts.com/css/background-image
- html style={{ + 'em' }}에러 - https://stackoverflow.com/questions/43366026/react-inline-style-style-prop-expects-a-mapping-from-style-properties-to-value
- css 이미지 사이즈 - https://nykim.work/86
- 구글 한글 폰트 - https://fonts.google.com/?subset=korean
- 깔끔한 폰트 - https://spoqa.github.io/spoqa-han-sans//ko-KR/
- 깔끔한 폰트 모음 - https://velog.io/@untiring_dev/HTMLCSS-Font-%EA%B4%80%EB%A0%A8-%EB%A7%81%ED%81%AC-%EB%AA%A8%EC%9D%8C
- 그림 이모티콘 - https://kr.piliapp.com/emoji/google/
- -
회원관리(S1-B01)
회원가입 (S1-P11)
1. Form Validation
- <input> onChange 이벤트
- 이름이 입력되지 않았을 때 -> is-invalid '이름을 입력하세요'
- 이름이 일정 글자 수를 초과했을 때 -> is-invalid '1자~20자 미만으로 작성하세요.'
- 이메일이 입력되지 않았을 때 -> is-invalid '이메일을 입력하세요'
- 이메일 형태에 맞지 않을 때 -> is-invalid '이메일 형식에 맞지 않습니다.'
- 이메일은 작성했는데 중복체크를 하지 않았을 때 -> is-invalid '이메일 중복체크를 하세요.'
- 비밀번호를 입력하지 않았을 때 -> is-invalid '비밀번호를 입력하세요'
- 비밀번호가 일정 글자 수를 초과했을 때 -> is-invalid '1자 이상~20자 미만으로 작성하세요.'
- 비밀번호 확인이 일치하지 않을 때 -> is-invalid '비밀번호가 일치하지 않습니다.'
- form <button> Submit 이벤트
- 이름이 입력되지 않았을 때
- 이메일이 입력되지 않았을 때
- 이메일 중복체크를 하지 않았을 때
- 비밀번호를 입력하지 않았을 때
- 비밀번호 확인을 입력하지 않았을 때 -> is-invalid '비밀번호가 일치하지 않습니다.'
- email 중복 체크 이벤트
- 이메일 중복체크를 통과하면 -> state.email_check = true '사용 가능한 이메일 입니다.'
- 중복 된 이메일이라면 -> is-invalid '이미 존재하는 이메일 입니다.'
Signup.js
더보기import React from "react"; import './Signup.css'; import {call, signup} from "../../service/ApiService"; class Signup extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); this.state = { email_check: false }; } handleSubmit(event) { event.preventDefault(); let form = document.getElementById('signup-form'); let input_name = document.getElementById('name-input'); let input_email = document.getElementById('email-input'); let input_pwd = document.getElementById("pwd-input"); let input_pwdh = document.getElementById("pwdh-input"); const data = new FormData(event.target); const name = data.get('uname'); const email = data.get('uemail'); const pwd = data.get('upwd'); const pwdh = data.get('upwdh'); console.log('회원가입 버튼 클릭!'); if(name.length === 0){ console.log('-이름 미입력!'); input_name.childNodes[0].focus(); input_name.childNodes[2].innerHTML = '이름을 입력하세요.'; form.classList.add('was-validated'); return; } if(email.length === 0){ console.log('-이메일 미입력!'); input_email.childNodes[0].focus(); input_email.childNodes[2].innerHTML = '이메일을 입력하세요.' form.classList.add('was-validated'); return; } if(pwd.length === 0){ console.log('-비밀번호 미입력!'); input_pwd.childNodes[0].focus(); input_pwd.childNodes[2].innerHTML = '비밀번호를 입력하세요.'; form.classList.add('was-validated'); return; } if(pwdh !== pwd){ console.log('-비밀번호 확인 불일치!'); input_pwdh.childNodes[0].focus(); form.classList.add('was-validated'); return; } if(!(this.state.email_check)){ console.log('-이메일 중복체크 미확인!') input_email.classList.remove('is-valid'); input_email.childNodes[0].classList.add('is-invalid'); input_email.childNodes[2].innerHTML='이메일 중복체크를 하세요.'; return; } signup({mname: name, memail: email, mpw: pwd}) .then((response) => { window.location.href="/login"; }) } onChangeEvent = (e) => { let input_name = document.getElementById('name-input'); let input_email = document.getElementById('email-input'); let input_pwd = document.getElementById("pwd-input"); let input_pwdh = document.getElementById("pwdh-input"); if(e.target.id === 'uemail' && this.state.email_check===true){ e.target.classList.remove('is-valid'); e.target.classList.add('is-invalid'); input_email.childNodes[1].innerHTML = ''; this.setState({email_check: false}); } if(e.target.id === 'uname' || e.target.id === 'upwd'){ if(e.target.value.length > 20){ console.log('글자 초과!'); console.log(e.target); e.target.classList.add('is-invalid'); console.log(e.target); if(e.target.id === 'uname') input_name.childNodes[2].innerHTML = '1자 이상~ 20자 미만으로 작성하세요.'; if(e.target.id === 'upwd') input_pwd.childNodes[2].innerHTML = '1자 이상~ 20자 미만으로 작성하세요.'; } else{ console.log('글자 미초과!'); e.target.classList.replace('is-invalid', 'is-valid'); e.target.classList.add('is-valid'); } } if (e.target.value.length === 0){ e.target.classList.replace('is-valid', 'is-invalid'); switch (e.target.id){ case 'uname': input_name.childNodes[2].innerHTML = '이름을 입력하세요.'; break; case 'uemail': input_email.childNodes[2].innerHTML = '이메일을 입력하세요.'; break; case 'upwd': input_pwd.childNodes[2].innerHTML = '비밀번호를 입력하세요.'; break; } } if(e.target.id === 'uemail' && e.target.value.length > 0 && !(e.target.value.includes('@'))){ e.target.classList.add('is-invalid'); input_email.childNodes[2].innerHTML = '이메일 형식에 맞지 않습니다.' } else if(e.target.id === 'uemail' && (e.target.value.includes('@'))) { e.target.classList.replace('is-invalid', 'is-valid'); } if(input_pwd.childNodes[0].value !== input_pwdh.childNodes[0].value){ input_pwdh.childNodes[0].classList.add('is-invalid'); } else { input_pwdh.childNodes[0].classList.replace('is-invalid', 'is-valid'); } } checkEmailDuplicate = (event) => { console.log('이메일 중복확인!'); let email_form = document.getElementById('email-input'); let email = document.getElementById('uemail'); if(email.value.length === 0){ console.log('-이메일 미입력!'); email_form.childNodes[0].focus(); email_form.childNodes[2].innerHTML = '이메일을 입력하세요.' email.classList.add('is-invalid'); } else if(this.state.email_check===false){ call("/auth/emailcheck", "POST", email.value).then((response)=>{ if(!response){ console.log('-사용 가능!'); email_form.childNodes[1].innerHTML = '사용 가능한 이메일 입니다.'; email_form.childNodes[2].innerHTML = ''; email.classList.remove('is-invalid'); email.classList.add('is-valid'); this.setState({email_check: true}); } else { console.log('-사용 불가능!'); email_form.childNodes[0].focus(); email_form.childNodes[2].innerHTML = '이미 존재하는 이메일 입니다.'; email.classList.add('is-invalid'); this.setState({email_check: false}); } }) } } render() { return( <div id="signup" className="w3-display-container"> <div id="signup-box" className="w3-container w3-center w3-display-middle"> <div id="card" className="w3-card-4 w3-margin w3-white"> <p id="title" className="w3-center w3-padding-32">회원가입</p> <div className="w3-center"> <form id="signup-form" onSubmit={this.handleSubmit} action="/action_page.php" className="needs-validation" noValidate> <label>이름</label> <div id="name-input" className="form-row"> <input type="text" className="form-control" id="uname" placeholder="이름을 입력하세요." onChange={this.onChangeEvent} name="uname" required/> <div className="valid-feedback"></div> <div className="invalid-feedback">이름을 입력하세요.</div> </div> <label>이메일</label> <div id="email-input" className="form-row"> <input type="text" className="form-control" id="uemail" placeholder="이메일을 입력하세요." onChange={this.onChangeEvent} name="uemail" required/> <div className="valid-feedback"></div> <div className="invalid-feedback">이메일을 입력하세요.</div> <button onClick={this.checkEmailDuplicate} id="dupl-btn" type="button" class="btn btn-danger">중복확인</button> </div> <label>비밀번호</label> <div id="pwd-input" className="form-row"> <input type="text" className="form-control" id="upwd" placeholder="비밀번호를 입력하세요." onChange={this.onChangeEvent} name="upwd" required/> <div className="valid-feedback"></div> <div className="invalid-feedback">비밀번호를 입력하세요.</div> </div> <label>비밀번호 확인</label> <div id="pwdh-input" className="form-row"> <input type="text" className="form-control" maxLength='15' id="upwdh" placeholder="비밀번호를 입력하세요." onChange={this.onChangeEvent} name="upwdh" required/> <div className="valid-feedback"></div> <div className="invalid-feedback">비밀번호가 일치하지 않습니다.</div> </div> <div className="w3-center w3-padding-32"> <button id="submit-btn" type="submit" className="btn btn-secondary">회원가입</button> </div> </form> </div> </div> </div> </div> ) } } export default Signup;
※JavaScript 에서 requestBody로 보낸 String 쿼리는, JAVA에서 " " 큰 따옴표에 감싸져 도착한다.※
=> 그러므로 JAVA에서 따옴표를 제거 후 SQL 쿼리문의 props로 보내줘야 한다.
(이것 때문에 이메일 중복 체크가 자꾸 오류 났다. 이미 존재하는 이메일인데 ""에 감싸진 채로 문자열을 비교해서, 존재하지 않는 이메일이라고 인식한 거다.)
-참고-
더보기- input 태그 autofocus - http://www.tcpschool.com/html-tag-attrs/input-autofocus
- 리액트 생성자 함수 - https://devlog.jwgo.kr/2018/08/20/set-state-undefined-error-in-react/
- classList 추가/삭제/수정 - https://hianna.tistory.com/469
- <input> validation is-valid - https://samtao.tistory.com/43
- JAVA 문자열에서 따옴표 제거 - https://hianna.tistory.com/523
- -
로그인/로그아웃 (S1-P14)
Login.js
더보기import React from "react"; import './Login.css'; import {signin} from "../../service/ApiService"; class Login extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(event) { event.preventDefault(); const data = new FormData(event.target); const email = data.get("uemail"); const pwd = data.get("upwd"); console.log(pwd); const input_email = document.getElementById("email-input"); const input_pwd = document.getElementById("pwd-input"); console.log('로그인 버튼 클릭!'); if(!(signin({memail: email, mpw: pwd}))){ console.log('-로그인 실패!'); input_email.childNodes[1].classList.add("is-invalid"); input_pwd.childNodes[1].classList.add("is-invalid"); } } render() { return( <div id="login" className="w3-display-container"> <div id="login-form" className="w3-container w3-center w3-display-middle"> <div id="card" className="w3-card-4 w3-margin w3-white"> <p id="title" className="w3-center w3-padding-32">로그인</p> <div className="w3-center"> <form onSubmit={this.handleSubmit} action="/action_page.php" className="needs-validation" noValidate> <div id="email-input" className="form-row"> <label>이메일</label> <input type="text" className="form-control" id="uemail" placeholder="이메일을 입력하세요." name="uemail" required/> <div className="valid-feedback"></div> <div className="invalid-feedback">이메일 또는 비밀번호가 일치하지 않습니다.</div> </div> <div id="pwd-input" className="form-row"> <label>비밀번호</label> <input type="text" className="form-control" id="upwd" placeholder="비밀번호를 입력하세요." name="upwd" required/> <div className="valid-feedback"></div> <div className="invalid-feedback">이메일 또는 비밀번호가 일치하지 않습니다.</div> </div> <div className="w3-center w3-padding-32"> <button type="submit" className="btn btn-secondary">로그인</button> </div> </form> </div> </div> </div> </div> ) } } export default Login;
-참고-
더보기- div안 <span> 수직정렬 (사용하진 않음) - https://appsnuri.tistory.com/403
- -
'Studying > Proj 과정' 카테고리의 다른 글
디자인 시스템 구축하기 (0) 2023.05.25 프로젝트 요구사항 분석&플래닝 연습하기 (0) 2023.05.12 [WebKIT640] 부트캠프 웹킷640 자체 홈페이지 (0) 2022.10.24 HANSOT 홈페이지 (0) 2022.09.04 도서관 홈페이지 만들기 (0) 2022.08.22