728x90
반응형

개발 97

[썬카/백엔드] 차량 판매등록 기능 구현 - 더미 데이터 생성과 객체지향적 설계

차량 판매등록 기능을 구현하며 객체지향적 설계를 한 것에 대해 정리해 보고자 한다. 차량 등록에 필요한 엔티티는 10개이며, 판매되는 자동차 1대가 10개 엔티티의 데이터를 전부 필요로 한다. 현재 차량 정보를 조회하는 카히스토리 api는 도입하지 않았기 때문에(너무 비싸..) 도입했다는 가정 하에 api 리턴값과 동일한 형태의 테이블을 설계했고, 그에 맞는 더미 데이터 생성 로직을 작성했다. 그 더미 데이터를 이용해 차량 등록 기능을 구현해 보았다. 다만 엔티티가 너무 많았기 때문에 코드가 길어졌고, 하나의 서비스 클래스에 이 모든 로직을 (엔티티 저장, dto변환, 더미데이터 생성 등) 담는 건 매우 좋지 않은 설계라고 생각되었다. 따라서 각 엔티티별 서비스 클래스에 create 메서드를 각각 만들고..

[썬카/백엔드] DTO <-> 엔티티 변환을 위한 매퍼 클래스 도입 및 적용

차량 판매등록 기능을 구현하는데, 차량 관련 엔티티 10개에 대한 반복적인 작업을 해야 했다.각 엔티티 만들고, 서비스 인터페이스 + 구현체 만들고, DTO 만들고... 전부 하나하나 일일이 해야 했다. 최대한 효율적으로 작업할 수 있는 방법은 없을까 많이 고민했지만, 엔티티 자체가 많은 거라 어쩔 수 없이 반복작업이 필요한 부분이었다. 그래도 유지보수성을 조금이라도 올리기 위해, facade 서비스 클래스를 따로 만들어서 여러 엔티티를 거치는 비즈니스 로직은 여기서 처리하고, 나머지 각 엔티티 별 서비스에서는 최대한 단일 책임 원칙(SRP)을 지켜 해당 엔티티에만 영향을 주는 로직을 작성하기로 했다. 그러나 정말 무의미하다고 느껴졌던 부분이, DTO 엔티티를 변환하기 위한 팩토리 메서드를 작성할 때였..

[썬카/백엔드] 차량 목록 조회 쿼리 작성 과정에서의 N+1 문제 발생과 해결

신뢰성이 중요한 중고차 거래에 필요한 차량 정보를 담기 위해서는 꽤 많은 테이블이 필요했다.차량 목록 조회 기능을 구현하는 것에만 5개의 테이블이 엮여있었고, 5개 테이블을 전부 조인해서 데이터를 가져와야 했다. 썬카는 차량 한대 당 메인 이미지 1개와 나머지 이미지들이 존재하며, 대표 이미지 여부는 is_primary 필드로 구분된다.메인 이미지는 가장 먼저 화면에 보여야 하기 때문에, 나머지 이미지들과 dto 별도의 필드에 담길 필요가 있었다.따라서 처음엔 메인 이미지를 조회한 후 나머지 이미지를 추가로 조회하는 방식으로 진행했었는데, 이 과정에서 N + 1 문제가 발생했다. N + 1문제- 최초 1번의 쿼리로 N개의 레코드를 조회한 후, 추가로 N개의 쿼리를 실행하게 되는 상황- 자동차가 10대라면..

[썬카/백엔드] Swagger를 이용한 API 명세서 도입

백엔드로써 프론트엔드와 협업하기 위해, 커뮤니케이션 비용을 줄인 효율적인 업무를 위해 API 명세서는 반드시 필요하다고 할 수 있다. 명확하게 정리된 API 명세서가 있다면 프론트엔드는 해당 자료를 참고하여 좀 더 수월하게 작업할 수 있고, 서로 엇갈려 불필요한 오류를 맞이할 일도 줄어든다.  1. 의존성 추가// build.gradledependencies { // 생략.. implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0'} build.gradle에 swagger 사용을 위한 springdoc-openapi 의존성을 추가한다. 단 여기서 스프링 부트 버전과 openapi 버전이 반드시 호환되어야 한다.각 스프링 부트 버전마..

[썬카/백엔드] dotenv를 사용한 중요 데이터 환경변수화

# application.propertiesspring.application.name=suncar# DBspring.datasource.url=jdbc:mysql://localhost:3307/suncarspring.datasource.username=rootspring.datasource.password=0000spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.jpa.hibernate.ddl-auto=updatespring.jpa.show-sql=true# JWTjwt.secret=BTpHjyFDurb0PefLrKP+e4vKKwNCw+rUoMEpQjde7T6EzB9QjtoqC7MFVcHwbFPr+r4/OlR9ZiB5rpG0axOd4Q..

[썬카/정기 회고] 스프린트 1 종료

기능 명세서 단위로 봤을 때, 구현한 기능은 2개 뿐이다. 로그인 그리고 회원가입.무슨 이거 2개 만드는데 3주나 걸렸냐 싶겠지만, 진짜 오래 걸린 것 맞다. 처음 진행하는 개인 프로젝트다 보니 생각보다 기능구현 외에 할 일이 정말 많다는 것을 느꼈다.회사에서 잠깐 일을 했더라도, 전부 짜여진 코드의 틀 위에서 약간의 기능 추가와 수정, 관리 정도였고.. 아예 환경세팅부터 시작하는 것은 처음이었으니 그럴 만도 하겠다. 지난 3주를 돌아보면 나는 단 하루도 쉰 날이 없는데 왜 이렇게 지체되었을까 라는 의문이 든다.아침에 일어나 운동을 갔다오고, 밥 먹고 하루종일 컴퓨터 앞에만 앉아있는데 말이다. 뭐 예비군도 갔고, 가끔 술도 마셨고 그러긴 했지만 그건 큰 핑계가 되지 못한다. 3주는 너무 길다.기간이 지체..

[썬카/백엔드] Spring Security를 이용한 JWT Access Token 발급 및 검증 로직 작성

매 요청마다 인가된 사용자가 요청하는 것인지 확인하기 위하여, 로그인 성공 시 JWT Access Token을 발급하고 사용자 요청 시 마다 토큰을 함께 전달받아 검증하는 로직을 추가하기로 했다. Spring Boot에서 JWT Access Token의 발급 및 검증은 Spring Security를 이용해 구현할 수 있다. 근데 생각보다 많이 복잡했다. Spring Security- Spring 기반 애플리케이션의 인증(Authentication)과 인가(권한 부여, Authorization)를 위한 보안 프레임워크 Spring Security - 특징- 포괄적인 보안 기능 : 인증, 권한 부여, 세션 관리, CSRF 보호 등 다양한 보안 기능 제공- 필터 체인 아키텍처 : 들어오는 HTTP 요청은, 여..

[썬카/백엔드] Soft delete 구현과 Base Entity 작성 및 적용

중고차 거래 플랫폼은 가치가 큰 물건을 거래하는 만큼 신뢰성 있는 데이터가 중요하기에, 실제로 데이터를 삭제하지 않고 논리적으로만 삭제하는 Soft delete 방식이 적합하다고 생각했다. 실수로 데이터가 삭제되어도 쉽게 복구할 수 있기 때문에 데이터 관리와 안정성이 중요한 경우 적합한 방식이라고 할 수 있다. Soft delete- 데이터베이스에서 레코드를 물리적으로 삭제하지 않고, 논리적으로 삭제된 것처럼 표시하는 기술- 일반적으로 테이블에 is_deleted 등의 필드를 추가하여 삭제된 상태를 표시한다. Soft delete - 필요성- 안전한 데이터 관리 : 우발적인 데이터 손실 방지- 데이터 복구 용이성 : 필요시 간단히 플래그만 변경하여 복구 가능- 유연한 데이터 접근 : 필요에 따라 삭제된 ..

[썬카/백엔드] 예외 전역 핸들러 및 커스텀 예외 구현

비즈니스 로직에서 발생하는 모든 예외를 캐치하고 일관된 형태로 응답하기 위해서, 전역 핸들러와 커스텀 예외를 구현했다.예외 처리 로직을 비즈니스 로직으로부터 분리할 수 있고, 클라이언트는 일관된 형태의 예외를 응답받기 때문에 예외에 대한 처리를 유연하게 할 수 있게 된다.  예외 전역 핸들러(Global Exception Handler)- 애플리케이션에서 발생하는 모든 예외를 일관되게 처리하는 메커니즘- Rest api를 구현한 Spring Boot에서는 @RestControllerAdvice 어노테이션으로 이를 구현한다. 예외 전역 핸들러 - 필요성- 코드 중복 제거 : 각 서비스마다 예외 처리 로직을 반복할 필요 없음, try-catch 블록 최소화- 일관된 오류 응답 : 모든 API에서 일관된 형태..

[Baekjoon 6603 / Python / 실버2] 로또

기본적인 백트래킹 문제이다.주어진 집합에서 6개 숫자의 조합(순서가 다른 같은 숫자를 중복으로 간주)을 구해야 한다. # 실버2 -> 백트래킹import sysinput = sys.stdin.readlinedef back_tracking(depth, start, result):    # 6개의 숫자가 모이면 출력    if depth == 6:        print(*result)        return        for i in range(start, len(S)):        result.append(S[i])        back_tracking(depth + 1, i + 1, result)        result.pop()while True:    K, *S = map(int, input..

728x90
반응형