안녕하세요 이번에는 1박2일 해커톤을 처음 참여해보게 되었는데요.
열심히 몬스터를 마시면서 아이디어 1시간만에 낸 기획 예진님 최고! 쏘큣 쏘쿨~ 고퀄의 디자인을 뚝딱 만들어낸 디자인 한솔님! erd작성, foodRegister api와 food inquiry api 를 작성한 큐티 mz 리치님~! 백엔드 최고! jwt와 유저 정보를 맡아 너무 고생하시고ㅠㅠ정말 열심히 힘내주신 sunset님!
엄청난 속도로 작업해주신 뚜루미님! 민수님!
너무너무 즐거웠습니다~
배운 것)
jwt과 interupt에 대해
~인증인가가 필요한 타이밍에 interupt를 일으키는 handler를 통해 유지보수를 간편이 하고 보안을 향상시킵니다.
resource의 파일 불러오기
package nerdinary.hackathon.domain.rate.rateService;
import static nerdinary.hackathon.global.exception.ErrorCode.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import nerdinary.hackathon.domain.food.repository.FoodRegisterRepository;
import nerdinary.hackathon.domain.rate.dto.RateResponse;
import nerdinary.hackathon.domain.login.service.UserException;
import nerdinary.hackathon.domain.user.User;
import nerdinary.hackathon.domain.user.UserRepository;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import org.springframework.core.io.Resource;
@Component
@RequiredArgsConstructor
public class RateService {
private final UserRepository userRepository;
private final FoodRegisterRepository foodRegisterRepository;
// JSON 데이터를 담을 리스트 (캐싱용)
private List<Map<String, Object>> rateDataList;
// 초기화 시 JSON 파일을 읽어서 rateDataList에 저장
@PostConstruct
public void init() {
try {
ObjectMapper mapper = new ObjectMapper();
// ClassPathResource를 사용해서 resources 폴더 내부 파일 읽기
Resource resource = new ClassPathResource("rateJson/rate-date.json");
InputStream inputStream = resource.getInputStream();
rateDataList = mapper.readValue(inputStream, new TypeReference<>() {});
System.out.println("rate-data.json 로드 완료!");
} catch (Exception e) {
throw new RuntimeException("rate-data.json 파일 로드 실패", e);
}
}
public RateResponse calculateUserRate(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserException(USER_NOT_FOUND));
long usedCount = foodRegisterRepository.countByUserAndFoodStatus(user, "사용");
long totalCount = foodRegisterRepository.countByUser(user);
int consumptionRatePercent = 0;
if (totalCount > 0) {
consumptionRatePercent = (int) ((double) usedCount / totalCount * 100);
}
String consumptionRate = consumptionRatePercent + "%";
// rateDataList에서 조건에 맞는 항목 찾기
Map<String, Object> matchedRate = findMatchingRate(consumptionRatePercent);
if (matchedRate == null) {
// 조건에 맞는 데이터가 없으면 빈 값 반환
return RateResponse.builder()
.consumptionRate(consumptionRate)
.fridgeComment("")
.nearExpiredCount(0)
.level(0)
.typeName("")
.foodBTI("")
.foodBTIDetail("")
.description("")
.build();
}
// 각 필드 안전하게 추출
String fridgeComment = (String) matchedRate.getOrDefault("fridgeComment", "");
int level = (int) ((Number) matchedRate.getOrDefault("level", 0)).intValue();
String typeName = (String) matchedRate.getOrDefault("typeName", "");
String foodBTI = (String) matchedRate.getOrDefault("foodBTI", "");
String foodBTIDetail = (String) matchedRate.getOrDefault("foodBTIDetail", "");
String description = (String) matchedRate.getOrDefault("description", "");
long expiredCount = foodRegisterRepository
.countByUserAndExpirationDateBeforeAndFoodStatus(user, LocalDate.now(), "보관");
return RateResponse.builder()
.consumptionRate(consumptionRate)
.fridgeComment(fridgeComment)
.nearExpiredCount(expiredCount) // 현재 로직에 근거 없음
.level(level)
.typeName(typeName)
.foodBTI(foodBTI)
.foodBTIDetail(foodBTIDetail)
.description(description)
.build();
}
/**
* consumptionRatePercent에 맞는 rateData 항목을 찾는 메서드
*/
private Map<String, Object> findMatchingRate(int consumptionRatePercent) {
for (Map<String, Object> rate : rateDataList) {
Map<String, Object> conditions = (Map<String, Object>) rate.get("conditions");
Integer min = (Integer) conditions.getOrDefault("consumptionRateMin", null);
Integer max = (Integer) conditions.getOrDefault("consumptionRateMax", null);
boolean minOk = min == null || consumptionRatePercent >= min;
boolean maxOk = max == null || consumptionRatePercent <= max;
if (minOk && maxOk) {
return rate;
}
}
return null;
}
}
devOps만 열심히 한 것 같지만 같이 개발하고 역경을 헤쳐나가는 매우 즐거운 시간이었습니다. CORS오류가 해결되지 않았을 때는 식겁했지만 임기응변으로 어떻게 할 수 있어 다행이었씁니다.
같은 팀의 모두 덕분에 24시간만에 완성도 높은 작품을 만들어 낼 수 있었습니다. 모두 너무 너무 감사했습니다~~!!
https://github.com/orgs/nerdinary-hackathon-8th/repositories
nerdinary hackathon 8th
너디너리 해커톤 8th O조라고팀. nerdinary hackathon 8th has 2 repositories available. Follow their code on GitHub.
github.com
'대외활동 > UMC 8기' 카테고리의 다른 글
9주차. API & Paging (0) | 2025.06.02 |
---|---|
8주차. API & Swagger & Annotation (0) | 2025.05.27 |
7주차. API 응답 통일 & 에러 핸들러 (0) | 2025.05.11 |
6주차. JPA 활용 (0) | 2025.05.06 |
5주차. springboot에서 매핑하기(양방향, 단방향) (0) | 2025.04.29 |