냉장고를 부탁해!
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
- -