서버 어플리케이션에서 DTO를 만들어서 사용하는 것은 아주 흔한 일이다. 이 글에서는 필자가 서버 어플리케이션을 개발할 때 사용하는 DTO 명명법과 역할에 대한 생각을 공유하고자 한다. # DTO의 명칭 필자는 DTO 이름을 명명할 때 다음과 같은 관례를 따르도록 작성하고 있다. ~Reqeust 요청 query param이나 request body, 컨트롤러가 받는 요청 객체 ex) UpdateUserNicknameRequest ~Response 응답 DTO ex) UpdateUserNicknameResponse Request,Response에는 꼭 행위(Create, Update, Get)를 붙힌다. 파일 정렬등을 고려하면 도메인명을 앞으로 빼도 무방하다 (UserUpdateNicknameRequest..
FastAPI에서 AWS S3에 이미지 업로드하는 방법을 공유한다 # S3 버킷 생성 버킷 생성 부분은 따로 다루지 않겠다. 구글에 검색하면 자료가 많이 나오니 참조하면 될 것 같다. 퍼블릭 엑세스를 모두 허용했는데도 access denied가 뜬다면, 권한 정책을 잘못 설정한 것일 가능성이 있으므로 다음을 체크해본다. https://dev.classmethod.jp/articles/if-access-denied-appears-even-though-you-disabled-public-access-blocking-on-amazon-s3-what-is-the-workaround/ Amazon S3에서 퍼블릭 액세스 차단을 비활성화 했음에도 AccessDenied이 뜨는 경우 해결 방법은? | Develo A..
코루틴은 동시성 프로그래밍을 실현시키고자 할 때 주로 고려되는 요소이다. 자바에서는 스레드 풀을 이용해서 이를 구현하거나, RxJava 라이브러리를 활용하면 된다. 코틀린에서는 언어에서 지원하는 훨씬 쓰기 편한 코루틴을 활용할 수 있다. 보통 코틀린은 안드로이드에서 주로 사용되는 언어라 API call이나 뷰 렌더링을 코루틴으로 비동기적으로 수행하는데, 서버에서는 어떤 방식으로 활용하면 좋을지 알아보자 MySQL, Spring MVC를 사용하는 환경이라고 가정한다. # 수행 시간이 오래걸리는 조회 API 성능 개선하기 다음과 같은 유저 상세조회 API를 생각해보자. 1. 유저가 쓴 게시물 목록 디비 조회 (5초) 2. 유저가 쓴 댓글 목록 디비 조회 (5초) 3. 유저의 특정 기간동안 거래내역 목록 디비..
기술면접 단골문제로도 등장하고 매번 헷갈리는 프로세스, 스레드, 코루틴, 동기, 비동기, 블로킹, 논블로킹, 동시성, 병렬성에 대해서 한 눈에 보기 쉽도록 정리한다. # 프로세스, 스레드, 코루틴 프로세스 실행 중인 프로그램 스레드 프로세스 내부에서 나뉘는 별도의 실행 단위 멀티스레드 : 한 프로세스 내부에서 여럿 스레드를 사용하는 것 하나의 프로세스 내부에서 여러개의 스레드가 실행 될 수 있다 코루틴 스레드 내부에서 나뉘는 별도의 실행 단위 하나의 스레드 내부에서 여러개의 코루틴이 실행 될 수 있다 코루틴은 코틀린 만의 개념이 아니라 동시성 프로그래밍의 패러다임이다 생성 비용 프로세스 > 스레드 > 코루틴 현실 예시) 음식점(서버 어플리케이션 = 프로세스)을 운영하고, 저녁 피크시간때 손님(트래픽)이 ..
프로젝트에서 복잡한 쿼리를 위해 보통 QueryDSL이나 MyBatis를 사용한다. MyBatis를 사용하는 경우 select 결과를 dto로 바로 받을 수 있는데, 이때 간혹 코틀린의 dto 클래스 (data class)의 프로퍼티 선언 순서와 select 절의 값 순서가 동일해야만 매핑되는 경우가 있다. 이런 경우는 어떤 경우이며 왜 그런지 살펴보자 (데이터베이스는 오라클을 기준) # 예시 세팅 다음과 같은 SQL을 가정해보자 SELECT A_V AS firstValue, B_V AS secondValue FROM table; 그리고 마이바티스로 이를 받아올 DTO는 다음과 같다고 해보자 data class Dto( val firstValue: String, val secondValue: String..
회사에서 사용하는 디비가 용량 이슈가 생겼던 적이 있는데, 그 당시 대강 보니 80TB 정도였다. 그래서 문득 1억건 정도 되는 데이터는 용량이 얼마나 될까?라는 생각을 가지게 되어서 테스트를 해보았다. 테이블 필드 타입, 수 그리고 인덱스 유무에 따라 다르겠지만, 그냥 가늠용으로 참고만 하면 좋을 것 같다. 디비는 MySQL이고 테스트 테이블은 위와 같이 생겼다. 여기에 write_date는 인덱스를 걸어놨다. 테스트 데이터는 7600만건 정도 넣어봤다. 해당 데이터는 AI허브에서 제공하는 인터넷 말뭉치 데이터인데, JSON형태로 제공해줘서 반복문으로 쉽게 집어넣었다. JDBC 배치 insert로 넣으니 50만건 넣는데 10초정도 걸렸다. (7600만 / 50만 = 152 * 10초 = 1520초 = ..
간혹 어플리케이션 외부에서 읽어오는 JSON 데이터 (외부 API나 JSON 파일 등)에서 JSON 포맷과 필드가 복잡한 경우가 많다. 이 경우 원하는 필드만 읽을 수 있도록 할 수도 있는데, 스프링 부트에서 기본적으로 내장되어있는 Jackson 라이브러리 설정을 다음과 같이 변경한다. @Configuration class JacksonConfig { @Bean fun objectMapper() = ObjectMapper().apply { configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) } } 예시로 대강 이런 JSON 파일을 읽어온다고 가정해보자 { "SJML": { "header": { "identifier": "텍스트_구어체 ..
JPA 자체만으로는서 동일한 키 값을 가지는 두 엔티티를 조회했을때 동일성을 보장해주지 않는다. 따라서 로직상 동일한 키를 가지는 두 엔티티를 비교하기 위해선 반드시 equals와 hashCode를 재정의해야한다. # 단일키일 경우 다음과 같은 PK로 하나의 값을 가지는 엔티티를 가정해보자. @Entity data class OneKeyEntity( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0L ) 이제 이 코드를 실행해서 리포지토리에서 가져온 동일한 두 ID 엔티티가 동일한지 확인해보면, val oneKeyFirst = oneKeyEntityRepository.findById(1).orElseThrow() val on..