전처리를 하니 정확도가 75% -> 28%가 되는 기이한 현상...
약 5개월간의 대장정이었던 캡스톤이 마침내 끝났다. '이게 될까?'라는 주제로 시작했던 프로젝트가, 서로 으쌰으쌰 하면서 각자 맡은 분야에서 최선을 다하니 '이게 되네!'라는 말로 마무리했던 아주 멋진 경험이었다. Spring에 감각이 생기고, Kubernetes로 인프라 및 CI/CD 까지 구축해서, 좀 더 스케일이 큰 배포까지 해본, 하고 싶은 건 다 할 수 있었던 경험이었다.
그러나, 본격적으로 프로젝트를 한 것은 처음이기에 많이 서툴렀다. 그리고 직접 구현하면서 꽤 많은 문제점은 해결되었지만, 아직 해결하지 못 한채로 남아있는 수수께끼들이 있다. 백엔드부터 시작해서 CI/CD, OCR, 그리고 인프라까지, 더 성장하기 위해서는 반드시 풀어야 할 의문점에 대해 나열해보려고 한다. 시험이 끝나고 한번 연구해볼 생각이다.
FavoriteFood favoriteFood = FavoriteFood.createFavoriteFood(createFavoriteFoodDto.getName(), user, food, createFavoriteFoodDto.getBaseNutrition());
@Transactional(readOnly = true)
public List<ResponseFoodDto> getFoodListByDate(Long userId, LocalDate date){
return foodList.stream()
.map(food ->
ResponseFoodDto.of(food.getId(),
food.getUser().getId(),
food.getName(),
food.getBaseNutrition(),
food.isFavorite(),
food.getAddedTime().getHour(),
food.getAddedTime().getMinute()
))
.collect(Collectors.toList());
}
객체를 업데이트 할 시, 객체에 직접 접근하는 update 메서드를 통해 값을 업데이트 했는데, 직접 객체에 접근하는 방법보다 더 개선할 방안은?
update, delete의 반환 타입은 void가 맞을까?
// 업데이트 반환값이 void다.
@Transactional
public void updateFood(UpdateFoodDto updateFoodDto, LocalDate date) {
...
}
// 삭제 반환값이 void다.
@Transactional
public void deleteFood(Long foodId, Long userId, LocalDate date) {
...
}
@Transactional
public Long saveFavoriteFood(CreateFavoriteFoodDto createFavoriteFoodDto) {
...
// Food와의 연관관계를 명시적으로 설정해준다.
food.setFavoriteFood(favoriteFood);
foodRepository.save(food);
...
}
2023-11-16 11:02:27.807 ERROR 1 --- [nio-8080-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet]
: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed; nested exception is org.springframework.data
.redis.serializer.SerializationException: Could not read JSON: Cannot
construct instance of `com.diareat.diareat.user.dto.response.ResponseRankUserDto
(no Creators, like default constructor, exist): cannot deserialize from Object
value (no delegate- or property-based Creator)
@Transactional
public void updateFavoriteFood(UpdateFavoriteFoodDto updateFavoriteFoodDto) {
...
// 연관관계가 있는 Food에 명시적으로 FavoriteFood의 속성을 바꿔준다.
food.updateFavoriteFood(updateFavoriteFoodDto.getName(), updateFavoriteFoodDto.getBaseNutrition());
...
}
@Transactional
public void deleteFavoriteFood(Long favoriteFoodId, Long userId) {
...
//모든 FavoriteFood 마다, 1:다 관계에 있는 Food와의 연관관계를 끊는다.
favoriteFood.getFoods().forEach(food -> food.setFavoriteFood(null));
...
}
@Transactional(readOnly = true)
public ResponseAnalysisDto getAnalysisOfUser(Long userId, int year, int month, int day){
...
// 한 달 전부터 시작해서 현재 날짜에 도달할 때까지 한 주 씩 더하여 반복문 실행
for (LocalDate date = currentDate.minusWeeks(3).with(DayOfWeek.MONDAY); date.isBefore(currentDate); date = date.plusWeeks(1)){
...
// 반복문 안의 한 주의 시작 (월요일)에서 하루 씩 더하여 반복문 실행
for (LocalDate inner_date = date; inner_date.isBefore(date.plusWeeks(1)); inner_date = inner_date.plusDays(1)){
// 칼, 탄, 단, 지의 영양소 합하기
...
}
// 안 쪽 반복문에서 합한 영양소를 배열에 추가하는 로직
...
}
...
}
// Food와의 연관관계를 명시적으로 설정해준다.
food.setFavoriteFood(favoriteFood);
public class ResponseSimpleFoodDto {} // Best 3 and Worst 3에 사용될 객체
public class ResponseFoodDto {} // 식습관 그래프나, 단순 음식 정보 반환 때 사용된 객체
api를 공인 도메인에 노출시키는 것은 위험하다. 실제 기업에서는 이를 보완하기 위해 어떤 방법을 사용할까?
회원가입 api와 로그인 api를 통해 임시의 JWT 토큰만 얻으면 모든 api에 접근할 수 있다. 좀 더 보안성 좋은 방법은 무엇일까?
Transactional을 사용해 테스트 코드를 작성하는 방법과, Mockito를 사용해 테스트 코드를 작성하는 방법 두 가지가 있었다. 이 둘은 각각 언제 사용해야 빛을 발할까?
from concurrent.futures import ProcessPoolExecutor
import asyncio
executor = ProcessPoolExecutor(max_workers=10)
async def run_in_executor(func, *args):
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(executor, func, *args)
return result
# FastAPI에서 영양성분표를 파싱하기 위해 multipart로 이미지 받아옴
@app.post("/parse_nutrients", status_code=201)
async def read_item(file: UploadFile = File(...)):
contents = await file.read()
nparr = np.frombuffer(contents, np.uint8)
#multipart 값을 이미지로 변환
image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
#멀티 스레딩 방식으로 이미지 처리
result = await run_in_executor(nutrition_run, image)
멀티 프로세스로 진행했더니 복수 요청에 대해 어느 정도 잘 처리했다. 그러나 CUDA out of memory 오류가 계속해서 발생하여, 어쩔 수 없이 싱글 프로세스로 진행했다. 이 이유는? 그리고 해결방안은?
멀티 프로세스, 멀티 스레드 방식을 파이썬에서는 어떻게 적절하게 적용할까?
OCR 추론의 경우 어떤 방식으로 해야 복수 개의 요청에 대해 효과적으로 처리할 수 있을까?
FastAPI를 사용할 때 S3 이미지를 다운 받고 처리했더니 지나치게 느렸다. 그래서 Multipart 방식으로 직접 이미지를 다운받았다. 뭔가 잘못된 방식이었을 것 같은데, 해결방안은?
Multipart 방식과 S3 방식의 장단점은 무엇일까?
Kubernetes는 과연 트레이드 오프가 없는 방안이었을까? Nginx & EC2 배포에 비해 Kubernetes 배포가 갖는 이점과 트레이드 오프는? (Traefik, Nginx)
자꾸만 완돌이가 죽어, 불안한 마음에 실제 LINC 사업단 경진대회에서는 EC2, RDS로 진행했다. 완돌이는 왜 자꾸 죽었을까? DHCP 오류였을까?
대다수의 기업들이 데이터베이스를 온프레미스 환경이 아닌 RDS와 같은 DB 전용 환경을 굳이 쓰는 이유는?
서버를 컨테이너화 시키는 것은 과연 배포단계에서의 Silver Bullet이라고 불릴 수 있을까? 서버 내에서 직접 코드를 실행하는 것과, 컨테이너로 실행하는 것의 장단점은 무엇일까?
파드 교체 과정에서 트래픽 정상 수신 여부를 확인하기 위해 readinessProbe를 사용했다. livenessProbe와 같이 사용하면 어떤 점에서 더 이점이 있을까?
Kubernetes를 적극 사용하는 기업의 경우도 Kustomization을 사용할까? 내가 구축한 Kubernetes 기반 CI/CD 파이프라인과 기업의 파이프라인에는 어떤 차이점이 있을까?
https://github.com/CAUSOLDOUTMEN
혹시 보시면서 해결 방안이 있다고 생각되시면, 자유롭게 댓글 달아주시면 정말로 감사드리겠습니다.