240809 TIL

송형근·2024년 8월 9일
0

TIL

목록 보기
10/43
post-thumbnail

스파르타 Java 단기 심화 과정


코드카타


프로그래머스 161989 덧칠하기

https://school.programmers.co.kr/learn/courses/30/lessons/161989

— 문제 설명

어느 학교에 페인트가 칠해진 길이가 n미터인 벽이 있습니다. 벽에 동아리 · 학회 홍보나 회사 채용 공고 포스터 등을 게시하기 위해 테이프로 붙였다가 철거할 때 떼는 일이 많고 그 과정에서 페인트가 벗겨지곤 합니다. 페인트가 벗겨진 벽이 보기 흉해져 학교는 벽에 페인트를 덧칠하기로 했습니다.

넓은 벽 전체에 페인트를 새로 칠하는 대신, 구역을 나누어 일부만 페인트를 새로 칠 함으로써 예산을 아끼려 합니다. 이를 위해 벽을 1미터 길이의 구역 n개로 나누고, 각 구역에 왼쪽부터 순서대로 1번부터 n번까지 번호를 붙였습니다. 그리고 페인트를 다시 칠해야 할 구역들을 정했습니다.

벽에 페인트를 칠하는 롤러의 길이는 m미터이고, 롤러로 벽에 페인트를 한 번 칠하는 규칙은 다음과 같습니다.

  • 롤러가 벽에서 벗어나면 안 됩니다.
  • 구역의 일부분만 포함되도록 칠하면 안 됩니다.

즉, 롤러의 좌우측 끝을 구역의 경계선 혹은 벽의 좌우측 끝부분에 맞춘 후 롤러를 위아래로 움직이면서 벽을 칠합니다. 현재 페인트를 칠하는 구역들을 완전히 칠한 후 벽에서 롤러를 떼며, 이를 벽을 한 번 칠했다고 정의합니다.

한 구역에 페인트를 여러 번 칠해도 되고 다시 칠해야 할 구역이 아닌 곳에 페인트를 칠해도 되지만 다시 칠하기로 정한 구역은 적어도 한 번 페인트칠을 해야 합니다. 예산을 아끼기 위해 다시 칠할 구역을 정했듯 마찬가지로 롤러로 페인트칠을 하는 횟수를 최소화하려고 합니다.

정수 nm과 다시 페인트를 칠하기로 정한 구역들의 번호가 담긴 정수 배열 section이 매개변수로 주어질 때 롤러로 페인트칠해야 하는 최소 횟수를 return 하는 solution 함수를 작성해 주세요.

— 제한 조건

  • 1 ≤ m ≤ n ≤ 100,000
  • 1 ≤ section의 길이 ≤ n
    • 1 ≤ section의 원소 ≤ n
    • section의 원소는 페인트를 다시 칠해야 하는 구역의 번호입니다.
    • section에서 같은 원소가 두 번 이상 나타나지 않습니다.
    • section의 원소는 오름차순으로 정렬되어 있습니다.

— 입출력 예

nmsectionresult
84[2, 3, 6]2
54[1, 3]1
41[1, 2, 3, 4]4

입출력 예 #1

  • 예제 1번은 2, 3, 6번 영역에 페인트를 다시 칠해야 합니다. 롤러의 길이가 4미터이므로 한 번의 페인트칠에 연속된 4개의 구역을 칠할 수 있습니다. 처음에 3, 4, 5, 6번 영역에 페인트칠을 하면 칠해야 할 곳으로 2번 구역만 남고 1, 2, 3, 4번 구역에 페인트칠을 하면 2번 만에 다시 칠해야 할 곳에 모두 페인트칠을 할 수 있습니다. !https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/7e657b3f-1e5b-4724-b053-9548b2cd17ba/img1.png 2번보다 적은 횟수로 2, 3, 6번 영역에 페인트를 덧칠하는 방법은 없습니다. 따라서 최소 횟수인 2를 return 합니다.

입출력 예 #2

입출력 예 #3

— 문제 풀이

import java.util.*;

class Solution {
    public int solution(int n, int m, int[] section) {
        List<Integer> sections = new ArrayList<>();
        for(int i=0;i<section.length;i++){
            sections.add(section[i]);
        }
        int answer = 0;
        while(!sections.isEmpty()){
            answer++;
            int cur = sections.get(0);
            while(!sections.isEmpty()&&sections.get(0)< cur+m){
                sections.remove(0);
            }
        }
        return answer;
    }
}

Redis 유저 추가 방법

Redis 버전 확인

  • Redis CLI에서 info server를 입력하면 설치된 Redis의 버전 확인이 가능함
    info server
    
    # 결과
    
    # Server
    redis_version:3.0.504
    redis_git_sha1:00000000
    redis_git_dirty:0
    redis_build_id:a4f7a6e86f2d60b3
    redis_mode:standalone
    os:Windows  
    arch_bits:64
    multiplexing_api:WinSock_IOCP
    process_id:4540
    run_id:168b92a05b8685b1051beace227cad1c33d43ffe
    tcp_port:6379
    uptime_in_seconds:349751
    uptime_in_days:4
    hz:10
    lru_clock:11892359
    config_file:C:\Program Files\Redis\redis.windows-service.conf

Redis 버전 변경

  • acl 명령어로 새 유저를 생성하기 위해 Redis 버전 기존 3.0.504에서 7.4.0-alpine으로 버전 업
  • 이전에는 Windows에 직접 설치해서 사용했지만 이번에 버전업을 하게 되면서 Podman desktop을 통해 컨테이너로 Redis 설치

Redis 사용자 추가

  • 사용자 추가는 ACL ( Access Controll List ) 를 통해 할 수 있음
  • 사용자 목록 조회
    • acl list
      acl list
      1) "user default on {password} ~* &* +@all"
  • 사용자 추가
    • acl setuser
      acl setuser {username} on|off >{password} {key 권한} {command 권한}
    • on | off - 사용자 계정 활성화 비활성화 설정
    • >{password} - 계정 비밀번호 설정
    • key 권한
      • allkeys(~*) : 모든 권한
      • ~{pattern}* : 특정 패턴으로 시작하는 키
    • command 권한
      • allcommands(+@all)
      • nocommands(-@all)
      • +@{command} : command 권한 추가
      • -@{command} : command 권한 제거
      • +@{group} : group 권한 추가
      • +@{group} : group 권한 제거
      • 그룹 권한 확인
        • acl cat
          acl cat
          1) "keyspace"
          2) "read"
          3) "write"
          4) "set"
          5) "sortedset"
          6) "list"
          7) "hash"
          8) "string"
          9) "bitmap"
          10) "hyperloglog"
          11) "geo"
          12) "stream"
          13) "pubsub"
          14) "admin"
          15) "fast"
          16) "slow"
          17) "blocking"
          18) "dangerous"
          19) "connection"
          20) "transaction"
          21) "scripting"
        • acl cat {group} ( ex) acl cat dangerous )
  • 일반 R/W 사용자 추가
    acl setuser newuser on >newpass allkeys allcommands -@dangerous

더 많은 정보


MSA 고민 포인트

💡 생각해낸 답이 맞는 지 모르겠어서... 지적은 늘 환영입니다.

MSA 환경에서 서로의 서비스를 의존하게 되는 의존성 순환이 발생했을 때 어떻게 해결할 수 있을까?

  • 의존성 순환이 발생하게 되는 경우, 각 서비스 로직간 경계를 제대로 나눠 순환을 제거하는 방법과 서비스가 강하게 연결되어있는 경우엔 서비스를 병합하는 것도 고려해볼만 한것 같습니다. 그리고 서비스에 여러 기능이 들어가있다면 서비스를 조금 더 작게 분할하는 것도 한 방법이 될 수 있습니다.

분산 시스템에서의 데이터 일관성을 어떻게 유지할 수 있을까?

  • 강력한 일관성을 자랑하는 2PC(2 Phase Commit) 방식과 최종 일관성을 보장하는 SAGA 패턴이 있습니다. 2PC는 커밋 이전까지 완료한 다음 트랜잭션 관리 서비스에 Prepare상태에 대해 알려주고 모든 DB가 준비가 되었을 때 Commit을 진행하게 되는 패턴입니다. 그리고 SAGA 패턴은 각 서비스 트랜잭션을 실시하고 실패하는 트랜잭션이 있을 경우 보상 트랜잭션을 실시하는 패턴입니다.
  • https://www.samsungsds.com/kr/insights/mas_data.html

레거시 모놀리식 서비스를 MSA로 전환한다고 할 때, 어떠한 이유가 있을까?

  • MSA 전환을 고려하는 경우, 도메인이 빠르게 변화하고 있어 그에 발맞춰 서비스를 업데이트할 수 있어야 하는 상황이라 생각합니다. Monolithic과 Micro Service 두 아키텍처의 가장 큰 차이가 확장성과 유연성이라고 생각하기 때문에, 기존 서비스에 빠르고 많은 변화가 필요할 때 MSA로의 전환을 고려해야 한다고 생각합니다.
  • https://www.atlassian.com/microservices/microservices-architecture/microservices-vs-monolith

프로젝트 관리 - 빌드 및 배포

Docker

  • App을 쉽게 만들고, 테스트하고, 배포할 수 있게 도와주는 소프트웨어 플랫폼
  • App을 컨테이너라는 가볍고 이식성 있는 패키지로 실행할 수 있게 해줌
  • Docker Image는 App을 실행하는 데 필요한 모든 것(코드, 런타임, 시스템 도구, 시스템 라이브러디 등)을 포함

Docker의 주요 특징

  • 컨테이너화 : App과 필요한 모든 것을 하나의 패키지로 묶어 어디서든 실행할 수 있음
  • 경량 : Docker는 운영 체제의 커널을 공유하므로, 가상 머신보다 훨씬 가볍고 빠르게 실행
  • 이식성 : Docker 컨테이너는 어디서든 동일하게 실행됨
  • 확장성 : Docker를 사용하면 여러 개의 컨테이너를 효율적으로 관리하고 쉽게 확장할 수 있음

Docker의 주요 용어

  • 이미지 (Image)
    • App과 그 실행에 필요한 모든 것을 포함하는 읽기 전용 템플릿. 이미지에는 실행에 필요한 모든것이 포함됨
    • 이미지는 컨테이너를 생성하기 위한 청사진 역할을 함
  • 컨테이너 (Container)
    • 컨테이너는 Docker 이미지를 실행한 상태.
    • 컨테이너는 격리된 공간에서 App을 실행하며, 실행에 필요한 모든 의존성을 포함하고 있음
    • 하나의 시스템에서 여러 개의 컨테이너를 독립적으로 실행할 수 있음
  • Dockerfile
    • Docker 이미지를 생성하기 위한 스크립트. 이미지를 빌드하는 데 필요한 명령어들이 포함되어 있음
    • Dockerfile을 사용하면 이미지 생성 과정을 자동화하고 일관되게 만들 수 있음
  • Docker Hub
    • Docker 이미지를 공유하고 저장하는 중앙 저장소. 사용자는 Docker Hub에서 다양한 공개 이미지를 다운로드 하거나 자신의 Repository에 이미지를 업로드할 수 있음
  • 볼륨 ( Volume )
    • 볼륨은 컨테이너의 데이터를 지속적으로 저장할 수 있는 메커니즘. 컨테이너가 삭제되더라도 볼륨에 저장된 데이터는 유지됨
  • 네트워크 (Network)
    • 컨테이너간의 통신을 관리하는 방식. 여러가지 네트워크 드라이버를 제공하여 다양한 네트워크 설정을 지원
    • 기본적으로 모든 컨테이너는 브릿지 네트워크를 통해 통신할 수 있음
    • 네트워크 종류
      • Bridge Network (브릿지 네트워크)
        • 기본적으로 Docker가 컨테이너를 실행할 때 사용하는 네트워크

        • 동일한 브릿지 네트워크에 연결된 컨테이너들은 서로 통신할 수 있음

        • 외부 네트워크와는 NAT ( 내부 네트워크의 여러 장치가 하나의 공용 IP 주소를 통해 외부 네트워크와 통신할 수 있도록 IP 주소를 변환하는 기술 ) 를 통해 통신

        • 일반적으로 단일 호스트에서 여러 컨테이너를 연결할 때 사용됨

        • 명시하지 않으면 모두 브릿지 네트워크에서 실행됨

          # 예시
          docker network create my-bridge-network
          docker run -d --name conatiner1 --network my-bridge-network nginx
          docker run -d --name conatiner2 --network my-bridge-network nginx
      • Host Network (호스트 네트워크)
        • 컨테이너가 호스트의 네트워크 스택을 직접 사용

        • 네트워크 격리가 없기 때문에 성능상 이점이 있지만, 보안 및 네트워크 충돌 위험이 있음

        • 일반적으로 성능이 중요한 App에 사용됨

          # 예시
          docker run -d --network host nginx
      • Overlay Network (오버레이 네트워크)
        • 여러 Docker 호스트에 걸쳐 있는 컨테이너를 연결할 때 사용
        • Swarm 모드 (Docker 컨테이너의 오케스트레이션과 클러스터링을 지원하여 여러 호스트에서 컨테이너를 관리하고 배포할 수 있는 기능)나 Kubernetes같은 오케스트레이션 도구와 함께 사용됨
        • 데이터 센터 또는 클라우드 환경에서 분산 시스템을 구축할 때 유용

Docker와 VM(가상 머신)

  • Docker의 장단점

    • 장점
      • 빠른 시작 시간과 낮은 오버헤드 : Docker 컨테이너는 App만 실행하고, OS의 핵심 부분은 공유하므로, VM보다 훨씬 빠르게 시작할 수 있음
      • 높은 이식성과 확장성 : Docker 컨테이너는 한 번 만들면 어디서든지 동일하게 실행됨. 여러 컨테이너를 쉽게 추가하고 관리할 수 있어, 필요에 따라 App을 확장하기 쉬움
    • 단점
      • 보안 격리가 VM보다 약함 : Docker 컨테이너는 동일한 OS 커널을 공유하기 때문에, VM보다 보안 격리 수준이 낮음. 하나의 컨테이너에서 커널에 영향을 미치는 문제가 발생하면, 다른 컨테이너에도 영향을 줄 가능성이 있음
      • 운영 체제 종속성 존재 : Docker는 리눅스 커널을 사용하여 작동하므로, 리눅스 OS에서 가장 잘 동작. 윈도우나 Mac 같은 다른 OS에서는 호환성 문제가 있을 수 있고, 리눅스 커널을 에뮬레이션하는 방식으로 작동해야 하기 때문에 성능이 저하될 수 있음
  • VM의 정의와 장단점

    • VM은 하이퍼바이저를 통해 물리적 하드웨어 위에 가상화된 OS를 실행하는 기술
      • 하이퍼바이저는 여러 OS를 동시에 실행할 수 있도록 물리적 하드웨어를 가상화하는 소프트웨어
    • 장점
      • 격리된 환경 제공 : 각 VM은 완전히 독립된 OS를 실행하므로, 하나의 VM에서 문제가 발생해도 다른 VM이나 호스트에 영향을 주지 않음
      • 다양한 OS 실행 가능 : VM을 사용하면 한 물리적 서버에서 여러 OS를 동시에 실행할 수 있음.
    • 단점
      • 오버헤드가 크고, 느린 부팅 시간 : VM은 전체 OS를 실행해야 하기 때문에, 많은 메모리와 CPU 자원을 소비함. VM을 부팅할 때 OS를 처음부터 시작해야 해서 시간이 오래걸림
      • 높은 리소스 소비 : VM은 각각 독립된 OS를 포함하므로, 여러 VM을 실행하면 메모리 CPU와 같은 자원을 많이 소모하게 되고, 이는 전체 환경의 성능 저하로 이어질 수 있음

Docker Compose란?

  • 다중 컨테이너 Docker App을 정의하고 실행하기 위한 도구
  • docker-compose.yaml 파일 하나로 App의 서비스, 네트워크, 볼륨 등을 정의할 수 있음

동일 Docker 네트워크 내 다른 컨테이너 서비스 요청 실습

App 생성

  • 의존성
    • Spring Web
    • Lombok
    • Open Feign
  • application.properties
    • project A
      spring.application.name=service-a
      
      server.port=8080
      service.b.url=http://service-b:8080
    • project B
      spring.application.name=service-b
      
      server.port=8080
  • 서비스 작성
    • project A
      • AApplication
        @SpringBootApplication
        @EnableFeignClients
        public class AApplication {
        
        	public static void main(String[] args) {
        		SpringApplication.run(AApplication.class, args);
        	}
        
        }
      • BServiceClient
        @FeignClient(name = "service-b", url = "${service.b.url}")
        public interface BServiceClient {
        
            @GetMapping("/hello")
            public String getHello();
        }
      • AController
        @RestController
        @RequiredArgsConstructor
        public class AController {
        
            private final BServiceClient bServiceClient;
        
            @GetMapping("/hi")
            public String hi() {
                String hello = bServiceClient.getHello();
                return "Service-a : hi ##### Service-b : " + hello;
            }
        }
    • project B
      • BController
        @RestController
        public class BController {
        
            @GetMapping("/hello")
            public String hello(){
                return "hello";
            }
        }

프로젝트 빌드

  • Dockerfile
    FROM openjdk:17-jdk-slim
    
    VOLUME /tmp
    
    ARG JAR_FILE=build/libs/*.jar
    
    COPY ${JAR_FILE} app.jar
    
    ENTRYPOINT ["java","-jar","/app.jar"]
  • docker build
    # service-a 디렉토리 내에서
    ./gradlew clean bootJar
    docker build -t img-service-a .
    # service-b 디렉토리 내에서
    ./gradlew clean bootJar
    docker build -t img-service-b .

Docker 네트워크 생성

docker network create my-network

컨테이너 실행

docker run -d --name service-b --network my-network -p 18080:8080 img-service-b
docker run -d --name service-b --network my-network -p 18081:8080 img-service-b
  • Service A의 hi 호출 결과

Docker Compose 활용

  • docker-compose.yml 작성
    version: '3.8'
    
    services:
      service-a:
        image: img-service-a
        ports:
          - "18080:8080"
        environment:
          - SERVICE_B_URL=http://service-b:8080
        depends_on:
          - service-b
    
      service-b:
        image: img-service-b
        ports:
          - "18081:8080"
    
    networks:
      default:
        driver: bridge
  • Docker Compose를 활용하면 docker-compose 파일 하나로 여러 컨테이너를 관리할 수 있음
  • network를 따로 생성하고 설정해주지 않아도 docker-compose를 활용하면 네트워크를 새로 생성하고 컨테이너들이 해당 네트워크를 사용한다.
profile
기록을 남겨보자

0개의 댓글