ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 냉장고를 부탁해!
    Studying/Proj 과정 2022. 10. 3. 22:15

     

    Github

    https://github.com/Kim-DaHam/SWEDU_2022_RecipeApp_FrontEnd

     

     

     

     

    실행 화면

     

    메인 화면

     

    회원가입 화면

     

     

    로그인 화면

     

     

    냉장고 화면

     

     

    레시피 화면

     

     

    레시피 등록 화면

     

     

    레시피 상세보기 화면

     

     

     

    1. 분석단계 - 요구사항명세서

    2022-10-03-MON 에 최종 승인 완료

    개별프로젝트_요구사항명세서_20190117김다함v2.0.hwp
    0.03MB

     

     

    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;
    }

    -참고-

    더보기
    1. 스프링부트 Entity 테이블 Date 타입 - https://jojoldu.tistory.com/527
    2. 스프링부트 LocalDate, LocalTime, LocalDateTime - https://footprint-of-nawin.tistory.com/67
    3. 뭘 잘못 건들여서... FridgeEntity.java를 못 찾는다 그런 식의 에러가 뜸 - 프로젝트 우클릭>Java Build Path>Libraries>Classpath에 FridgeEntity 뭐가 있길래 Remove > 다시 실행 > 성공
    4. 외래키 Join - https://siyoon210.tistory.com/27
    5. -

     

    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();
    	}
    }

    -참고-

     

    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;
    }

    -참고-

    더보기
    1. java 제네릭(Generic)타입 - https://st-lab.tistory.com/153
    2. -

     

    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.");
    		}
    	}
    }

    -참고-

    더보기
    1. java Optional - https://mangkyu.tistory.com/70
    2. JPA findBy~ 메소드 - https://devfunny.tistory.com/426    https://exhibitlove.tistory.com/262
    3. delete 기능 에러(cannot reliably process 'remove' call) - https://csy7792.tistory.com/134
    4. -

     

    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);
    		}
    	}
    }

    -참고-

    더보기
    1. 현재시간 구하기 - https://hianna.tistory.com/607
    2. -

     

     

    레시피 관리(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;
    }

    -참고-

    더보기
    1. 외래키 - https://brunch.co.kr/@dan-kim/26
    2. 자바 패키지가 폴더로 변했을 때 - https://flea.tistory.com/7
    3. JPA 엔티티 어노테이션 - https://cjw-awdsd.tistory.com/46
    4. -

     

    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.");
    		}
    	}
    }

     

    -참고-

     

    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

    -참고-

    홈 화면

    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로 정해져있어야 한다!!!

     

    -참고-

    더보기

     

    회원관리(S1-B01)

    회원가입 (S1-P11)

    1. Form Validation

    • <input> onChange 이벤트
      1. 이름이 입력되지 않았을 때 -> is-invalid '이름을 입력하세요'
      2. 이름이 일정 글자 수를 초과했을 때 -> is-invalid '1자~20자 미만으로 작성하세요.'
      3. 이메일이 입력되지 않았을 때 -> is-invalid '이메일을 입력하세요'
      4. 이메일 형태에 맞지 않을 때 -> is-invalid '이메일 형식에 맞지 않습니다.'
      5. 이메일은 작성했는데 중복체크를 하지 않았을 때 -> is-invalid '이메일 중복체크를 하세요.'
      6. 비밀번호를 입력하지 않았을 때 -> is-invalid '비밀번호를 입력하세요'
      7. 비밀번호가 일정 글자 수를 초과했을 때 -> is-invalid '1자 이상~20자 미만으로 작성하세요.'
      8. 비밀번호 확인이 일치하지 않을 때 -> is-invalid '비밀번호가 일치하지 않습니다.'
    • form <button> Submit 이벤트
      1. 이름이 입력되지 않았을 때
      2. 이메일이 입력되지 않았을 때
      3. 이메일 중복체크를 하지 않았을 때
      4. 비밀번호를 입력하지 않았을 때
      5. 비밀번호 확인을 입력하지 않았을 때 -> 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로 보내줘야 한다.

    (이것 때문에 이메일 중복 체크가 자꾸 오류 났다. 이미 존재하는 이메일인데 ""에 감싸진 채로 문자열을 비교해서, 존재하지 않는 이메일이라고 인식한 거다.)

     

    -참고-

    더보기
    1. input 태그 autofocus - http://www.tcpschool.com/html-tag-attrs/input-autofocus
    2. 리액트 생성자 함수 - https://devlog.jwgo.kr/2018/08/20/set-state-undefined-error-in-react/
    3. classList 추가/삭제/수정 - https://hianna.tistory.com/469
    4. <input> validation is-valid - https://samtao.tistory.com/43
    5. JAVA 문자열에서 따옴표 제거 - https://hianna.tistory.com/523
    6. -

     

     

     

    로그인/로그아웃 (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;

     

    -참고-

    더보기
    1. div안 <span> 수직정렬 (사용하진 않음) - https://appsnuri.tistory.com/403
    2. -

     

     

     

     

Designed by Tistory.