리스트 컴프리헨션

리스트를 초기화하는 방법 중 하나.

  • 대괄호 안에 조건문과 반복문을 적용하여 리스트를 초기화 할 수 있음
# 0부터 9까지의 수를 포함하는 리스트
array = [i for i in range(10)]
# range라는 함수는 0부터 9까지 i가 순회 할 수 있도록 해줌
# i라는 변수가 0부터 9까지 증가를 할 때마다, 그 i 값을 원소로 설정해서 리스트를 만듬

print(array)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 이렇게 유용한 게 있었다니…
# 0 부터 19까지의 수 중에서 홀수만 포함하는 리스트
array = [i for i in range(20) if i & 2 == 1]

#0 부터 19까지 홀수만 출력 됨
print(array)

# 1부터 9까지의 수들의 제곱 값을 포함하는 리스트
array = [i * i for i in range(1,10)]
# i라는 변수는 1부터 9까지 증가하며 i 값들을 제곱해서 새로운 리스트를 만듬

또한, 리스트 컴프리헨션은 2차원 리스트를 초기화 할 때 효과적으로 사용이 가능

특히 N X M 크기의 2차원 리스트를 한 번에 초기화 해야 할 때 매우 유용함.

  • 좋은 예시 : array = [[0] * m for _ in range(10)] → 반복 될 때 마다, m (열) 크기 만큼 리스트의 원소를 설정

  • n 번 반복을 할 때마다, 배열(가로) 길이가 m인 리스트(내용물이 0)를 생성함

언더바는 언제 사용 하는가?

파이썬에서는 반복을 수행하되, 반복을 위한 변수의 값을 무시하고자 할때 언더바(_)를 자주 사용한다고 한다.

# 1부터 9까지의 자연수를 더하기
summary = 0 
for i in range(1, 10):
		summary += i
# 반복문을 수행하며 , i값을 더하기 위해 
# i 값이 필요하니 변수를 만듦
# Hello World를 5번 출력

for _ in range(5):
		print("Hello World")

# 반복문을 돌며 특정한 값이 필요없고,
# 그냥 반복만 하고 싶을 경우이니 _ 
# 언더바 사용

튜플 자료형

튜플 자료형은 리스트와 유사하지만 다음과 같은 문법적 차이가 있다.

  • 튜플은 한 번 선언된 값을 변경할 수 없다.
  • 리스트는 대괄호 [] 를 이용하지만, 튜플은 소괄호 () 를 이용한다.

튜플은 리스트에 비해 상대적으로 공간 효율적이다.

# 튜플 사용 예제

a = (1, 2, 3, 4, 5, 6, 7, 8, 9)

# 네 번째 원소만 출력
print(a[3])

# 두 번째 원소부터 네 번째 원소까지
print(a[1:4])

튜플내의 값을 변경하려 한다면( ex : a[2] = 7),

tuple 객체의 값은 Immutable(불변)이니 변경하지 못한다고 에러가 뜸

튜플을 사용하면 좋은 경우

  1. 서로 다른 성질의 데이터를 묶어서 관리해야 할 때
    • 최단 경로 알고리즘에서는 (비용, 노드 번호)의 형태로 튜플 자료형을 자주 사용함
    • 예를 들어 , 학생의 학번, 성적과 같이 다른 성질의 데이터를 묶을 때. 다양한 정보를 포함 가능
  2. 데이터의 나열을 해싱의 키 값으로 사용해야 할 때
    • 튜플은 변경이 불가능하므로 리스트와 다르게 키 값으로 사용될 수 있다.
  3. 리스트보다 메모리를 효율적으로 사용해야 할 때 .

집합 자료형

집합은 다음과 같은 특징이 있다.

  • 중복을 허용하지 않음
  • 순서가 없음

집합은 리스트 혹은 문자열을 이용해서 초기화 할 수 있다.

  • 이때 set() 함수를 이용함.

혹은 중괄호안에 각 원소를 콤마(,)를 기준으로 구분하여 삽입함으로써 초기화 할 수 있다.

데이터의 조회 및 수정에 있어서 O(1)의 시간에 처리할 수 있다.

# 집합 자료형 초기화 방법 1
data = set([1, 1, 2, 3, 4, 5 ]) -> 중복이 제거 된 후, 집합 자료형으로 됨.

# 집합 자료형 초기화 방법 2
data = {1, 1, 2, 3, 4, 4, 5}

# 집합 자료형으 ㅣ연산

a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7}

# 합집합
print(a | b)
# {1, 2, 3, 4, 5, 6, 7}

# 교집합
print(a&b)
# {3, 4, 5}

# 차집합
print( a - b)
# {1, 2}

기본 입출력

input() 함수는 한 줄의 문자열을 입력 받는 함수.

map() 함수는 리스트의 모든 원소에 각각 특정한 함수를 적용할 때 사용.

# 공백을 기준으로 구분된 데이터를 입력 받을 때는 다음과 같이 사용
list(map(int, input().split()))

# 공백을 기준으로 구분된 데이터의 개수가 많지 않다면, 단순히 다음과 같이 사용
a, b, c = map(int, input().split()) -> 바로 a, b, c 변수에 할당 가능

빠르게 입력 받기

사용자로부터 입력을 최대한 빠르게 받아야 하는 경우가 있음

파이썬의 경우 sys 라이브러리에 정의돼 있는 sys.stdin.readline() 메서드를 이용함.

  • 단 입력 후 엔터가 줄 바꿈 기호로 입력되므로 rsstrip() 메서드를 함께 사용함.

조건문과 반복문

조건문에서 아무것도 처리하고 싶지 않을때 pass 키워드를 사용한다고한다.

예시 ) 디버깅 과정에서 일단 조건문의 형태만 만들어 놓고 조건문을 처리하는 부분은 비워놓고 싶은 경우

score = 85

if score >= 80:
  pass # 나중에 작성할 소스코드
else:
  print('성적이 80점 미만입니다')

print('프로그램을 종료합니다')

# 결과 : 프로그램을 종료합니다
# 파이썬 조건문 내에서으 ㅣ부등식

x = 15
if 0 < x < 20:
	print()

# 요런것도 가능하다구 한당 !

람다 표현식

람다 표현식을 이용하면 함수 간단하게 작성할 수 있음

  • 특정한 기능을 수행하는 함수를 한 줄에 작성할 수 있다는 점이 특징임.
# 람다 표현식으로 구현한 두 매개변수의 합 구하기
# 이름 없는 함수라고도 불림 (ㄷ ㄷ 처음 앎)
print((lambda a, b : a + b)(3,7))
# 예시: 내장 함수에서 자주 사용되는 람다 함수

array = [('홍길동', 50), ('이순신', 32)]
# 각 원소들이 튜플 형태로 구성되어 있음

print(sorted(array, key=lambda x : x[1])
# 어떠한 튜플이나 리스트와 같은 원소가 있을때, 얘의 두번째 (여기서는 점수 -> 1번째 값)
# 튜플의 2번째 값을 기준으로 정렳할 수 있음
# 정렬 기준 (key 속성)를 람다함수를 사용함.
# 여러 개의 리스트에 적용
list1 = [1,2,3,4,5]
list2 = [6,7,8,9,10]

result = map(lambda a, b: a+b, list1, list2)
# map 함수는 각각의 원소에 대해 어떠한 함수를 적용하는 것임
# 따라서 list1과 list2의 각각의 원소를 더함
# 각각의 순서에 맞는 값끼리 더함 -> 1 +6, 2+7, 3+8 ...

'Programming Language > Python' 카테고리의 다른 글

[ 자주 사용되는 표준 라이브러리 ]  (0) 2023.07.16

웹 서버 프로젝트를 pm2 로 관리하던 중 생긴 문제.

 

프로젝트 요구 사항 중, 사용자가 업로드 한 유튜브 영상의 조회수를 하루마다 갱신하여 DB에 저장해야하는 기능이 필요했다.

따라서 DB에 저장된 영상의 고유 id값을 가져와 YouTube API를 통해 조회수를 수집 후, 이를 다시 DB에 Insert 하는 API를 개발 하였다.

 

key_value는 영상 id, runtime은 해당 영상의 조회수.

따로 크롤러는 만들지 않고, 해당 프로젝트는 pm2 프로세스로 관리 되던 터라 직접 cron 라이브러리를 통하여 함수를 작성하였다.

  1.  DB에 저장된 비디오의 정보를 가져와 해당 비디오의 id값만을 따로 리스트로 만들어 valuesOfContentList에 저장해준다.
  2. valuesOfContentList getVideoViewsCount 함수를 통해 유튜브에서 해당 영상의 조회수를 가져온다.
  3. 1에서 가져온 id값과, 조회수를 각각 key, value형태로 만들어 DB에 새롭게 Insert 한다.
  4. 해당 함수를 node-cron 라이브러리를 통해 주기적으로 실행할 수 있도록 설정하였다.

해당 함수를 로컬에서 테스트 후, 잘 실행되는 걸 확인하였고 이에 안심하고 배포를 하게 된다...

그러나, 예상과는 다르게 영상별로 조회수가 22시에 한번씩만 Insert 돼야 했지만 한 영상마다 수집한 조회수가 15건이 되어버렸다..

중복으로 수집된 데이터로 인해, 영상별 조회수 통계를 보여주는 페이지에서 대혼돈이 찾아왔다...(눈물)

 

왜 로컬에선 딱 한번만 수집되고, 배포 환경에선 안 되는 걸까..라는 원인 모를 상황에 약 이틀 동안 머리를 싸맸다..

처음엔 코드 문제인가 싶어 다시 찬찬히 훑어보았지만 매우 정상적으로 잘 작동 했다..

 

그러자, 불현듯 스치는 생각

https://yunja.tistory.com/12 

 

[ Jenkins + pm2 ] 트러블 슈팅

사내 프로젝트를 젠킨스를 통해 배포 자동화를 구축 후, 문제가 발생하게 된다. 상기의 이미지처럼, Jenkins Build Step을 쉘 스크립트로 비교적 간단하게 작성했다. NestJS로 프로젝트를 구성했던 터

yunja.tistory.com

해당 포스팅에서 잠깐 언급이 되었는데, 잠깐 설명하자면 배포할 프로젝트는 pm2로 관리되며 이 pm2는 프로젝트 루트 경로의 ecosystem.config.js 파일을 토대로 인스턴스 갯수를 정하여 그에 맞게 프로세스가 실행된다.

 

module.exports = {
  apps: [
    {
      name: 'app',
      script: './dist/app.js',
      instances: 15,
      exec_mode: 'cluster',
    },
  ],
};

배포 환경에선 서버 인스턴스가 15개나 되어, 위에서 작성한 영상 조회수 수집 함수가 무려 15번이나 실행되었던 것이다 !!

 

따라서 나는 한 인스턴스에서만 조회수 수집 함수가 실행 되게 하고 싶었으며, 해당 방법을 모색해보았다.

module.exports = {
  apps: [
    {
      name: 'primary',
      script: './dist/app.js',
      instances: 1,
      exec_mode: 'cluster',
    },
    {
      name: 'replica',
      script: './dist/app.js',
      instances: 13,
      exec_mode: 'cluster',
    },
  ],
};

기존에는 동일한 이름의 인스턴스로 15개를 실행하였지만, 수정한 config.js는 

하나의 인스턴스에는 primary라고 이름을 지정해주고, 다른 인스턴스에는 replica라고 이름을 명시해주었다.

이렇게 되면, 배포 서버의 노드 프로세스는 primary 이름의 프로세스 1개, replica이름의 프로세스 13개가 실행되어 진다.

 

이에 맞게, node-cron 에서 실행되어지는 getVideoViewsCount 함수에도 현재 실행되는 프로세스 인스턴스의 이름이 primary 일 경우에만, 함수를 실행하도록 하였다.

 

함수 처음 부분에 process.env.name을 가져와 primary인지 검사하여 , 맞을 경우에만 함수를 실행하도록 변경하였다.

 

해당 함수까지 변경 후 ,처음 원했던 대로 정상적으로 22시에 함수가 정상적으로 primary 인스턴스에서만 실행되었다 !!!

'Trouble Shooting' 카테고리의 다른 글

GitActions 러너 환경을 arm64 EC2로 변경하기  (0) 2023.08.04
[ Jenkins + pm2 ] 트러블 슈팅  (0) 2023.07.15

사내 프로젝트를 젠킨스를 통해 배포 자동화를 구축 후, 문제가 발생하게 된다.

상기의 이미지처럼, Jenkins Build Step을 쉘 스크립트로 비교적 간단하게 작성했다.

 

NestJS로 프로젝트를 구성했던 터라,

  1. npm run build 후(TypeScript compile to JavaScript)
  2. 생성된 dist 디렉토리의 main.js 실행이 필요했기에 pm2 기본 config 파일을 생성(ecosystem.config.js)
  3. 해당 config.js 파일 내부에 필요한 인스턴스 갯수와, 실행 파일의 경로를 작성해주었다.
// root/ecosystem.config.js

module.exports = {
  apps: [
    {
      name: 'primary',
      script: './dist/main.js',
      instances: 1,
      exec_mode: 'cluster',
    },
    {
      name: 'replica',
      script: './dist/main.js',
      instances: 13,
      exec_mode: 'cluster',
    },
  ],
};

위의 단계를 통해 프로젝트 레포지토리에 푸쉬 이벤트 발생 시, 레포지토리에서 웹 훅을 발생시켜 Jenkins 서버에서 자동으로

위 첫번째 사진의 Build Step들이 실행되도록 구축하였다.

 

요약

  • Jenkins Build Step에서 노드 프로세스 매니저인 Pm2를 config.js 실행 시키도록 설정
  • 위의 Jenkins jobs 이 끝난 후, 배포 성공 여부를 슬렉 알림으로 받음
  • 배포 서버에 정상적으로 변경 사항이 반영된 것까지 확인 후, 모든 것이 끝났다고 생각했다.

그러나, 문제는 여기서 발생하게 된다.

  • 배포 서버의 pm2 로그 확인을 위해 ssh로 접속 후, pm2 logs를 확인 해보았지만 .. 아무것도 보이지 않았다
  • 뭔가 잘못된 것을 직감 하고 배포 된 웹서버에 접속해보았지만...이게 웬일 ? 실제로 웹은 정상적으로 동작하고 있었다. (ㄷㄷㄷ...)
  • 다시 말하자면, 젠킨스에서 pm2 start와 reload가 잘 일어 났지만, 서버의 pm2 list엔 전혀 찾아 볼 수가 없었다.

왜 이런일이 발생한걸까? 처음부터 접근 방법이 잘못 되었던 것이다.

 

Jenkins 잡에서 실행되는 pm2 프로세스는 서버 로컬 환경이 아닌 젠킨스 내의 새로운 세션에서 실행된다.

따라서 이 새로운 세션에서 pm2를 실행한 것이니, 로컬 환경에서 직접 프로세스의 목록을 확인이 불가능 했던 것이다.

 

또한 pm2는 현재 사용자 세션과 관련된 프로세스 목록만 표시하기 때문에, 젠킨스 내부의 새로운 세션에서 실행되는 프로세스는 해당 세션에 대한 정보를 표시 할 수 없으며, pm2 list에서 보이지 않았던 것이다.

 

서버 로컬 환경에서 pm2 list가 나오지 않았던 이유를 알게됐다.. 하지만 왜? 젠킨스는 이렇게 동작하는 걸까 ?

기존 처음 젠킨스를 접하고, 쉘 스크립트로 파이프라인을 구축했을 때, 해당 서버의 로컬 환경에서 정의된 쉘 스크립트를 그대로 실행하는 줄 알았다. 

그러나, 젠킨스 잡은 젠킨스에서 실행되는 작업 단위로, 일반적으로 빌드, 배포, 테스트 등의 작업만을 수행 한다.

 

위에 설명한 바와 같이 젠킨스 잡은 새로운 세션에서 실행되며, 이는 젠킨스 잡이 독립된 환경에서 실행되도록 하는 데 목적이 있다.

  1. 환경 분리 : 젠킨스 잡은 여러 개의 빌드 또는 배포를 병렬로 실행할 수 있다. 이를 위해 각 Job은 독립된 환경에서 실행되어야 한다.
    즉, 각 Job은 자체적인 프로세스 그룹 또는 세션에서 실행되어 서로의 작업에 영향을 주지 않는다.
  2. 격리와 안정성 : 새로운 세션에서 Jenkins Job을 실행함으로써, 현재 사용자 세션 또는 기존의 실행 중인 프로세스에 영향을 주지 않고 독립적으로 실행될 수 있다. 이렇게 함으로써 잡의 실행 중에 발생하는 예외나 에러가 다른 프로세스에 영향을 주지 않고 안정적으로 처리 될 수 있다.
  3. 자원 관리: 젠킨스 잡이 별도의 세션에서 실행되면, 해당 세션에 할당된 자원(메모리, CPU등) 을 더 효율적으로 관리가 가능하다.

따라서 젠킨스 잡은 새로운 세션에서 실행되며, 젠킨스에서 실행되는 pm2 프로세스는 새로운 세션에서 실행되어 pm2 list를 통해 직접 빌드 스크립트에서 확인을 해야한다.

 

젠킨스 pm2가 새로운 세션에서 동작한다는 것을 인지한 후, 해당 빌드 스크립트(젠킨스 잡)에서 프로세스 로그를 표현하기엔 보기가 상당히 불편했기 때문에 다른 방법을 모색 해보았다.

 

자동 업데이트 스크립트 구성하기

젠킨스에 의해 실행될 스크립트를 먼저 작성.

#!/bin/bash
sudo npm install # yarn이나 npm 둘 다 하나만
npm run build
pm2 restart ecosystem.config.js

해당 파일을 프로젝트 루트 경로에 생성 해주고, Jenkins 잡 빌드 스크립트에서 해당 파일을 실행 시켜준다면,

처음 원했던 대로 새로운 세션에서 실행되지 않고 서버 로컬 환경에서 pm2 프로세스로 실행이 되며 서버 로그를 쉽게 확인이 가능했다.. !

 

Terraform ?

  • Hashicorp에서 오픈 소스로 개발 중인 클라우드 인프라스트럭처 자동화를 지향하는 IaC 도구
  • HCL(Hashcorp Configuration Language) 언어를 사용하여 리소스를 선언
  • 인스턴스, 네트워크와 같은 low-level뿐 아니라 DNS, SaaS와 같은 high-level의 요소도 관리가 가능.

Terraform 특징

  • IaC
    • 인프라를 코드로 정의하여 생산성과 투명성을 높임.
    • 정의한 코드를 쉽게 공유하여 협업이 가능
  • Execution Plan
    • 사소한 변경이 인프라 전체에 어떤 영향을 미칠지 미리 확인이 가능함.
    • 종속성 그래프를 작성하여 이 그래프를 바탕으로 계획을 세우고, 적용되었을 때 변경되는 인프라 상태를 확인가능
  • Change Automation
    • 여러 장소에 같은 구성의 인프라를 구축하고 변경할 수 있도록 자동화가 가능
    • 인프라를 구축하는 데 드는 시간을 절약하고 사람이 직접 했을 때의 실수를 줄일 수 있음.

'''
알파벳 대문자와 숫자(0~9)로만 구성된 문자열이 입력으로 주어진다.
이때 모든 알파벳을 오름차순으로 정렬하여 이어서 출력한 뒤에, 그 뒤에 모든 숫자를 더한 값을
이어서 출력합니다.

예를 들어 K1KA5CB7이라는 값이 들어오면 ABCKK13을 출력합니다.
'''

def replace():
    strings = input()
    alphabet = []
    num = 0

    for string in strings:
        if string.isalpha():
            alphabet.append(string)
        else:
            num += int(string)
    alphabet.sort()
    result = ''.join(alphabet)
    print(result + str(num))
replace()

'알고리즘,자료구조' 카테고리의 다른 글

[왕실의 나이트]  (3) 2023.07.10
'''
주어진 8 X 8 체스판에서 , 특정한 좌표가 주어졌을 때,
나이트가 이동 가능한 경로의 경우의 수를 출력 하시오.

나이트는
수평으로 두칸 이동한 뒤에 수직으로 한 칸 이동하거나,
수직으로 두 칸 이동한 뒤에 수평으로 한 칸 이동할 수 있습니다.

행의 좌표는 1~8 까지로 표시하며, 열의 좌표는 a~h 까지로 표시합니다.
'''


def chess():
    count = 0
    information = input()
    row = int(information[1])
    # 열의 값은 a~h 까지의 문자로 주어지므로 아스키 코드로 변경 후 (예 : a = 97) a로 빼고 1을 더하면 해당 열의 값을 구할 수 있다.
    # information[0]가 c일 경우는 99 이므로, a=97을 뺀후 , 1을 더하면 열의 좌표인 3이 된다.
    colunm = int(ord(information[0])) - int(ord('a')) + 1

    # 나이트가 이동가능한 좌표를 튜플 리스트로 저장해놓는다.
    # ex) x 좌표가 -2 가 된다는 것은 위로 이동한다는 것, 또한 y 좌표가 1 이 되는 것은 오른쪽으로 이동
    steps = [(-2, 1), (-2, -1), (-1, 2), (1, 2), (2, -1), (2, 1), (1, -2), (-1, -2)]

    for step in steps:
        # 나이트가 이동 가능한지 체크
        # 들어온 행 값 ( information[1])에 x좌표를 더해 계산함
        next_row = row + step[0]
        # 들어온 열 값 ( information[1])에 y좌표를 더해 계산함
        next_colunm = colunm + step[1]

        # 이동 가능한 스텝은 체스 판 8x8 크기 안에 있어야 함.
        if 1 <= next_row <= 8 and 1 <= next_colunm <= 8:
            count += 1
    print(count)


chess()

'알고리즘,자료구조' 카테고리의 다른 글

문자열 재정렬  (0) 2023.07.11

대중적인 formatting rule과 linting rule로 설정함

(rule이 각자 다를 수 있으니 충분한 협의 후 코딩 rule마다 변경)

 

1. node package manager install : npm init -y

—> package.json

의존성 표시

 

2. Formatting: prettier

npm install --save-dev prettier

 

—>package.json (prettier install)

—>package-lock.json

 

3. prettier 사용 시 필요 (+ prettier Extension)

.prettierrc 파일 생성

<—.prettierrc—>

 

{

    "semi" : false,  

    "singleQuote": true

}

 

4. .vscode 폴더 생성 -> settings.json 파일 생성 ( 로컬 파일 세팅 모아둠, 본 프로젝트에만 쓰이는 세팅들 for javascript)

<—settings.json—>

{

    "[javascript]": {

        "editor.formatOnSave": true,

        "editor.defaultFormatter": "esbenp.prettier-vscode"

    },

    "[json]": {

      "editor.defaultFormatter": "esbenp.prettier-vscode",

      "editor.formatOnSave": true,

      "editor.tabSize": 2,

      "editor.detectIndentation": false,

      "editor.insertSpaces": true

    },

    "[jsonc]": {

      "editor.defaultFormatter": "esbenp.prettier-vscode",

      "editor.formatOnSave": true,

      "editor.tabSize": 2,

      "editor.detectIndentation": false,

      "editor.insertSpaces": true

    },

    "tailwindCSS.emmetCompletions": true  // tailwindCSS 사용시에만

}

5. eslint 설치 : npm install —save-dev eslint

 

5-1. eslint 사용 시 필요 —> eslint 설정 파일 생성

< — .eslintrc.js —>

module.exports = {

  root: true,

  parserOptions: {

    ecmaVersion: 2020,

  },

  extends: ['airbnb-base', 'plugin:node/recommended', 'prettier'],

  rules: {

    'import/prefer-default-export': ['off'],

  },

}

 

6.  airbnb-base linting rule 설치

npm install --save-dev eslint-config-airbnb-base eslint-plugin-import

 

  1. npm install --save-dev eslint-config-prettier
  2. npm install --save-dev eslint-plugin-node
  3. < — .eslintrc.js —>

module.exports = {

  extends: ['airbnb-base', 'plugin:node/recommended', 'prettier'],

}

7. prettier 확장자 설치 (+eslint)

 

8. type checking (  // @ts-check   )

 npm install --save-dev typescript

 

9. node환경에서 type checking

npm install --save-dev @types/node(노드에서 주로 사용되는 객체들의 타입정보)

 

10. jsconfig.json

<  jsconfig.json >

{

  "compilerOptions": {

    "strict": true,

    "noImplicitAny": true,

    "noUnusedParameters": true,

    "noUnusedLocals": true,

    "noUncheckedIndexedAccess": true

  },

  "include": ["src/**/*"],

  "exclude": ["node_modules"]

}

+ Recent posts