Studying/Proj 과정

냉장고를 부탁해!

Kim Da Ham 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. -