3/27(금)

dev_joo·2일 전

코드카타

최소직사각형

가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다.

처음에 size[][]배열의 w, h 중 가장 큰 값들을 가로, 세로 크기로 정하면 되겠다 했는데 명함을 돌렸을 때를 어떻게 처리하지 고민이 되었다.

입출력 예 #2
명함들을 적절히 회전시켜 겹쳤을 때, 3번째 명함(가로: 8, 세로: 15)이 다른 모든 명함보다 크기가 큽니다. 따라서 지갑의 크기는 3번째 명함의 크기와 같으며, 120(=8 x 15)을 return 합니다.

이걸 보고 명함 중 가장 큰 명함의 크기를 찾아내면 지갑의 크기를 알아낼 수 있다.
나는 가로*세로 = 면적 으로 면적이 가장 큰 명함을 찾아내기로 했다.

class Solution {
    public int solution(int[][] sizes) {
        //sizes의 원소는 [w, h] 형식입니다.
        // 가장 큰 명함의 크기 = 지갑의 크기 = 가장 면적이 큰 명함의 크기
        int maxArea = 0;
        for(int[] size : sizes) {
            if(size[0] * size[1] > maxArea) maxArea = size[0] * size[1];
        }
        return maxArea;
    }
}

❌ 지금 코드의 문제

if(size[0] * size[1] > answer)

이건 그냥 넓이만 비교하는 거라서
👉 실제 필요한 지갑 크기랑 전혀 관계가 없다.

[10, 1] → 10x1 = 10
[9, 9] → 9×9 = 81 선택
실제 → (10,1) 회전 가능 → (10,1)
→ 가로 max = 10, 세로 max = 9
→ 답 = 90

가장 큰 세로, 가로 길이를 구해 지갑의 크기 구하기

결국 맨 처음했던 생각이 좀 더 맞는 방향이었다.😂
단, 명함을 회전했을 경우를 생각해 최소 지갑 크기를 구하려면

(**긴 변**, 짧은 변)

처럼 긴 변을 한쪽으로 회전시킨 상태로 봐야한다.
하나의 반복문 안에서 카드를 회전해 (긴 변, 짧은 변) 상태로 만들면서
가장 긴 세로, 가장 긴 가로변 길이를 구하면 된다.

class Solution {
    public int solution(int[][] sizes) {
        int maxW = 0;
        int maxH = 0;
        
        for (int[] size : sizes) {
            // (w, h)가 (긴 변, 짧은변)이 되도록 회전시킨다.
            int w = Math.max(size[0], size[1]);
            int h = Math.min(size[0], size[1]);
            
            maxW = Math.max(maxW, w);
            maxH = Math.max(maxH, h);
        }
        
        return maxW * maxH;
    }
}


당연히 업체가 허브에 속해서 상품 업체 재고가 업체에 있으면 허브에 있는줄 알았는데...

재고는 허브에 있어야 한다.

우리 프로젝트에서 주문까지 확장될 염두를 하면 상품의 총 재고와 허브의 재고를 각각 가져야 한다.

API에서 필요한 API인지 확인하기

나는 다른 서비스에서 인증처리를 구현 테스트하려면 먼저 유저가 생성되어있어야 하기 때문에 테스트를 편하게 하기 위해 가짜 유저를 생성하는 MASTER 회원가입 API가 있으면 좋겠다고 생각했지만

스프링 시큐리티 테스트를 사용하면 가짜 유저가 있다 하고 그에 따른 인증 정보를 Mock으로 생성할 수 있기 때문에 괜찮다.
@WithMockUser
@WithUserDetails

시간이 되면 이벤트 처리에 따른 다이어그램 순서도 그려보기

테이블 구조에 대한 논의

지난 팀원들과 회의 때 처음엔 확정된 상태의 Order들만을 가지는 테이블을 나누어 관리해야한다고 생각했다.

문제 상황: FixOrder와 Order 테이블을 분리한 이유가 무엇인지 명확하지 않다

이렇게 문제 상황을 간단히 정리하니까 따로 두었을 때 장점이 보이지 않아 우선 FixOrder 테이블 없이 Order의 상태만 두고 조회하는것으로 일단락했다.

CQRS 쿼리 테이블

일과시간이 끝나고 따로 이에 대해서 따로 더 찾아봤다.
CQRS는 ~이다.

Command (쓰기) 와 Query (읽기) 를 분리하는 아키텍처
[Command 모델] → 상태 변경 (주문 생성, 상태 변경)
[Query 모델]   → 조회 최적화 (주문 목록, 최근 주문)

CQRS는 읽기와 쓰기의 요구사항 각각을 만족시키고 싶을 때 사용한다.

구분요구사항
쓰기정합성, 트랜잭션
읽기속도, 조회 최적화

상황정리

현재 MSA 서비스 주문 -> 배송

Order 서비스 → 주문 생성
        ↓ (이벤트)
Delivery 서비스 → 배달 생성

여기서 사용자에게 최근 주문 목록 기능을 제공할때

  • 주문 + 배달 상태 같이 실시간 조회

라는 요구사항이 발생한다.

DB (Order, Delivery)
   ↓
CQRS Query Table (order_query 테이블
order + delivery join된 형태로 미리 저장)
   ↓
Redis Cache (CQRS와 별개로 성능 향상을 위해 캐싱)

모놀리스의 중간 테이블과 다른 점은 ~이다.
조회하는 서비스(배송)에 두거나 별도 쿼리 서비스에 둔다.
별도 쿼리 서비스에 두게 되면 쿼리 테이블은 다음과 같이 작성된다.

Order Service (쓰기)
Delivery Service (쓰기)

        ↓ 이벤트

Query Service (읽기 전용)

User API, ERD 수정

User (Aggregate Root)
 ├── id (UUID)
 ├── username
 ├── email
 ├── slackId
 ├── Role (Value Object)
 │    └── MASTER | HUB_MANAGER | HUB_DELIVERY_MANAGER | COMPANY_DELIVERY_MANGER | COMPANY_MANAGER
 └── Status (Value Object) // Role 승인 여부에 따른 유저의 상태
      └── PENDING | APPROVED | REJECTED

기존에는 User 테이블에서 password를 관리했었는데,
Auth 서비스 쪽에서 Keycloak 인증 기능으로 회원가입을 완료하면 user객체가 완성되어 User서비스에서 조회를 지원하는 형태인줄 알았는데,

Auth 서비스의 기능 중 ‘인증(Authentication)’기능 자체를 외부(Keycloak)로 위임하는 것이기 때문에 Auth에서도, User 에서도 비밀번호를 가지고있지 않는다.

회원가입 요청에서도 페이로드에 비밀번호를 (과감히!) 제거한다.

다른 서비스와 연계 테스트를 하기 위해서 사용자를 임의로 생성하는 API가 구현되어있으면 좋겠다고 생각했는데,

테스트 단에서 Security Context 를 Mock으로 만들어 사용하면 다른 서비스와 관계없이 테스트를 할 수 있다고 하셨다.

profile
풀스택 연습생. 끈기있는 삽질로 무대에서 화려하게 데뷔할 예정 ❤️🔥

0개의 댓글