계정명 암호 이름 이메일등을 작성하게 되면 URL 설정하는 화면이 나오는데 EC2 instance를 종료시키기 전까지는 Public IP는 그대로이므로 그대로 진행하면 된다.
아래는 Jenkins 웹서비스 화면이다.
Test, build, Containerize, deploy 등의 모든 과정을 job으로 해서 Jenkins에 할당할 수 있다.
이러한 job을 실행시키는걸 Jenkins에서는 build 한다고 한다.
Tools
Maven
code를 compile하고 build하는 도구. 널리 쓰이는 다양한 라이브러리들을 관리하는 도구를 build 도구라고 하는데 maven도 이 중에서 하나. Maven은 java로 작성한 코드를 build하는 도구.
다양한 dependency들을 maven repository가 관리해준다. 따라서, 사용자들은 maven에 뭐가 필요한지 표시만 해주면 된다. 표시를 해주면 maven의 Repository에 가서 가져온다.
Gradle이라는 도구도 java & springboot framework 사용시 자주 사용되는 빌더인데 gradle도 maven repository에서 가져온다.
Jenkins는 Java로 만들어져있다. Jenkins가 Java Framework를 사용하는 프로젝트를 컨트롤하는 소프트웨어로 시작했지만 현재는 확장이 되어 다른 프로그래밍 언어에 대해서도 관리가 가능하다. 따라서, Maven Configuration이 default로 제공된다.
JDK
Worker node에서 다른 버전의 JDK를 사용하는 경우 JDK를 Add해줘야한다. 하지만 우리는 앞서 JDK 17을
SSH는 Secure Shell의 줄임말로, 원격 호스트에 접속하기 위해 사용되는 보안 프로토콜
*Shell(쉘): 명령어와 프로그램을 사용할 때 쓰는 인터페이스로 사용자로부터 명령을 받아 그것을 해석하고 실행
기존 원격 접속은 ‘텔넷(Telnet)’이라는 방식을 사용했는데, 암호화를 제공하지 않기 때문에 보안상 취약했다.
실제로 WireShark같은 패킷 분석 프로그램을 이용하면 누구나 쉽게 원격 접속 과정에서 옮겨지는 비밀번호나 파일 내용 등의 데이터를 탈취할 수 있다. 이러한 보안 문제를 해결하기 위해서 암호화 기술인 SSH가 등장했고 현재 원격 접속을 위해서 필수적이다.
클라우드 환경에 접속하기 위해서는 원격접속이 필수이므로 SSH도 그만큼 많이 사용하게된다.
SSH 작동 원리
SSH를 구성하는 가장 핵심적인 키워드는 ‘KEY(키, 열쇠)’입니다. 사용자(클라이언트)와 서버(호스트)는 각각의 키를 보유하고 있으며, 이 키를 이용해 연결 상대를 인증하고 안전하게 데이터를 주고 받게 된다. 키를 생성하는 방식은 두가지로 ‘대칭키’와 ‘비대칭키(또는 공개 키)’ 방식이다.
선물을 직접 전하기 힘들 때 카카오톡 선물하기 기능을 이용해 축하 선물을 보낼 수 있습니다. 당신의 친구들이 이번 달까지 선물을 주고받은 기록을 바탕으로 다음 달에 누가 선물을 많이 받을지 예측하려고 합니다.
두 사람이 선물을 주고받은 기록이 있다면, 이번 달까지 두 사람 사이에 더 많은 선물을 준 사람이 다음 달에 선물을 하나 받습니다.
예를 들어 A가 B에게 선물을 5번 줬고, B가 A에게 선물을 3번 줬다면 다음 달엔 A가 B에게 선물을 하나 받습니다.
두 사람이 선물을 주고받은 기록이 하나도 없거나 주고받은 수가 같다면, 선물 지수가 더 큰 사람이 선물 지수가 더 작은 사람에게 선물을 하나 받습니다.
선물 지수는 이번 달까지 자신이 친구들에게 준 선물의 수에서 받은 선물의 수를 뺀 값입니다.
예를 들어 A가 친구들에게 준 선물이 3개고 받은 선물이 10개라면 A의 선물 지수는 -7입니다. B가 친구들에게 준 선물이 3개고 받은 선물이 2개라면 B의 선물 지수는 1입니다. 만약 A와 B가 선물을 주고받은 적이 없거나 정확히 같은 수로 선물을 주고받았다면, 다음 달엔 B가 A에게 선물을 하나 받습니다.
만약 두 사람의 선물 지수도 같다면 다음 달에 선물을 주고받지 않습니다.
위에서 설명한 규칙대로 다음 달에 선물을 주고받을 때, 당신은 선물을 가장 많이 받을 친구가 받을 선물의 수를 알고 싶습니다.
친구들의 이름을 담은 1차원 문자열 배열 friends 이번 달까지 친구들이 주고받은 선물 기록을 담은 1차원 문자열 배열 gifts가 매개변수로 주어집니다. 이때, 다음달에 가장 많은 선물을 받는 친구가 받을 선물의 수를 return 하도록 solution 함수를 완성해 주세요.
제한사항
2 ≤ friends의 길이 = 친구들의 수 ≤ 50
friends의 원소는 친구의 이름을 의미하는 알파벳 소문자로 이루어진 길이가 10 이하인 문자열입니다.
이름이 같은 친구는 없습니다.
1 ≤ gifts의 길이 ≤ 10,000
gifts의 원소는 "A B"형태의 문자열입니다. A는 선물을 준 친구의 이름을 B는 선물을 받은 친구의 이름을 의미하며 공백 하나로 구분됩니다.
ryan은 선물을 더 많이 줬던 muzi에게서 선물을 하나 받고, 선물을 주고받지 않았던 neo보다 선물 지수가 커 선물을 하나 받습니다.
frodo는 선물을 더 많이 줬던 ryan에게 선물을 하나 받습니다.
neo는 선물을 더 많이 줬던 muzi에게서 선물을 하나 받고, 선물을 주고받지 않았던 frodo보다 선물 지수가 커 선물을 하나 받습니다.
다음달에 가장 선물을 많이 받는 사람은 ryan과 neo이고 2개의 선물을 받습니다. 따라서 2를 return 해야 합니다.
입출력 예 #2
주고받은 선물과 선물 지수를 표로 나타내면 다음과 같습니다.
↓준 사람 \ 받은 사람→
joy
brad
alessandro
conan
david
joy
-
0
0
0
0
brad
0
-
0
0
0
alessandro
1
1
-
1
1
conan
0
0
0
-
0
david
0
0
1
0
-
이름
준 선물
받은 선물
선물 지수
joy
0
1
-1
brad
0
1
-1
alessandro
4
1
3
conan
0
1
-1
david
1
1
0
alessandro가 선물을 더 많이 줬던 joy, brad, conan에게서 선물을 3개 받습니다. 선물을 하나씩 주고받은 david보다 선물 지수가 커 선물을 하나 받습니다.
david는 선물을 주고받지 않았던 joy, brad, conan보다 선물 지수가 커 다음 달에 선물을 3개 받습니다.
joy, brad, conan은 선물을 받지 못합니다.
다음달에 가장 선물을 많이 받는 사람은 alessandro이고 4개의 선물을 받습니다. 따라서 4를 return 해야 합니다.
입출력 예 #3
a와 b, a와 c, b와 c 사이에 서로 선물을 주고받은 수도 같고 세 사람의 선물 지수도 0으로 같아 다음 달엔 아무도 선물을 받지 못합니다. 따라서 0을 return 해야 합니다.
1차 시도
def solution(friends, gifts):
current_cnt = {}
next_cnt = {}
send_cnt = {}
receive_cnt = {}
present_idx = {}
for gift in gifts:
sender = gift.split()[0]
receiver = gift.split()[1]
send_cnt[sender] += 1
receive_cnt[receiver] += 1
current_cnt[gift] += 1
for friend in friends:
present_idx[friend] = send_cnt[friend]-receive_cnt[friend]
for i in len(friends):
for j in len(friends)-i:
A = friends[i]
B = friends[j]
type_A = str(A) + " " + str(B)
type_B = str(A) + " " + str(B)
if currend_cnt(type_A) > currend_cnt(type_B):
next_cnt[A] += 1
elif currend_cnt(type_A) < currend_cnt(type_B):
next_cnt[B] += 1
else:
if present_idx[A] > present_idx[B]:
next_cnt[A] += 1
elif present_idx[A] < present_idx[B]:
next_cnt[B] += 1
answer = max(next_cnt.values())
return answer
2차 시도(올클)
import numpy as np
def solution(friends, gifts):
lf = len(friends)
table_gift = np.zeros((lf,lf))
table_idx = np.zeros(lf)
find_max = np.zeros(lf)
#선물 주고 받은 표 데이터
for gift in gifts:
i = friends.index(gift.split()[0])
j = friends.index(gift.split()[1])
table_gift[i][j] += 1
#선물 지수 계산
table_idx[i] += 1
table_idx[j] -= 1
for i in range(lf):
for j in range(i+1,lf):
if table_gift[i][j] > table_gift[j][i]:
find_max[i] += 1
elif table_gift[i][j] < table_gift[j][i]:
find_max[j] += 1
else:
if table_idx[i] > table_idx[j]:
find_max[i] += 1
elif table_idx[i] < table_idx[j]:
find_max[j] += 1
answer = max(find_max)
return answer
테스트 1 〉 통과 (0.08ms, 28.3MB)
테스트 2 〉 통과 (0.13ms, 28.2MB)
테스트 3 〉 통과 (0.23ms, 28.6MB)
테스트 4 〉 통과 (0.17ms, 27.9MB)
테스트 5 〉 통과 (3.36ms, 28.7MB)
테스트 6 〉 통과 (0.42ms, 28.3MB)
테스트 7 〉 통과 (2.28ms, 28.1MB)
테스트 8 〉 통과 (2.02ms, 28.5MB)
테스트 9 〉 통과 (12.59ms, 28.7MB)
테스트 10 〉 통과 (11.59ms, 28.5MB)
테스트 11 〉 통과 (12.13ms, 28.5MB)
테스트 12 〉 통과 (8.71ms, 28.4MB)
테스트 13 〉 통과 (22.60ms, 28.9MB)
테스트 14 〉 통과 (21.58ms, 28.7MB)
테스트 15 〉 통과 (22.93ms, 28.6MB)
테스트 16 〉 통과 (23.13ms, 28.7MB)
테스트 17 〉 통과 (0.18ms, 28.2MB)
테스트 18 〉 통과 (13.12ms, 28.6MB)
테스트 19 〉 통과 (24.97ms, 28.6MB)
테스트 20 〉 통과 (9.72ms, 28.3MB)
테이블 생성
3차 시도(올클) - for 문으로 리스트 초기화
def solution(friends, gifts):
length = len(friends)
table_gift = [[0 for _ in range(length)] for _ in range(length)]
table_idx = [0 for _ in range(length)]
find_max = [0 for _ in range(length)]
#선물 주고 받은 표 데이터
for gift in gifts:
i = friends.index(gift.split()[0])
j = friends.index(gift.split()[1])
table_gift[i][j] += 1
#선물 지수 계산
table_idx[i] += 1
table_idx[j] -= 1
for i in range(length):
for j in range(i+1,length):
if table_gift[i][j] > table_gift[j][i]:
find_max[i] += 1
elif table_gift[i][j] < table_gift[j][i]:
find_max[j] += 1
else:
if table_idx[i] > table_idx[j]:
find_max[i] += 1
elif table_idx[i] < table_idx[j]:
find_max[j] += 1
answer = max(find_max)
return answer
테스트 1 〉 통과 (0.04ms, 10.2MB)
테스트 2 〉 통과 (0.05ms, 10.2MB)
테스트 3 〉 통과 (0.10ms, 10.3MB)
테스트 4 〉 통과 (0.07ms, 10.1MB)
테스트 5 〉 통과 (1.46ms, 10.2MB)
테스트 6 〉 통과 (0.18ms, 10.2MB)
테스트 7 〉 통과 (1.01ms, 10.1MB)
테스트 8 〉 통과 (0.95ms, 10.2MB)
테스트 9 〉 통과 (4.41ms, 10.5MB)
테스트 10 〉 통과 (5.47ms, 10.4MB)
테스트 11 〉 통과 (5.47ms, 10.5MB)
테스트 12 〉 통과 (3.90ms, 10.5MB)
테스트 13 〉 통과 (12.97ms, 10.7MB)
테스트 14 〉 통과 (15.82ms, 10.6MB)
테스트 15 〉 통과 (12.88ms, 10.6MB)
테스트 16 〉 통과 (14.04ms, 10.8MB)
테스트 17 〉 통과 (0.08ms, 10.1MB)
테스트 18 〉 통과 (4.50ms, 10.5MB)
테스트 19 〉 통과 (13.11ms, 10.4MB)
테스트 20 〉 통과 (2.65ms, 10.3MB)
사용 기술
선형 탐색
2차원 배열 데이터베이스 구현
Trouble Shooting
dict 타입이 hash 기반이라 더 빠르다고 dict를 쓰려고 억지를 부리다 시간이 오래걸렸다. 2차원 배열도 충분히 좋으니까 쉬운 문제에서는 2차원 배열로 구현하자
Main Point
numpy로 배열 초기화하는거보다 2중 for 문이 더 빠르다
조건 따질때는 2차원 배열도 좋은 옵션이다
list의 .index(값) 메서드를 사용하면 index(몇번째 원소인지)를 알아낼 수 있다.
iterm2를 새로 설치하게 되면서 기존 디폴트 쉘이 bash에서 zsh로 변경되었다. 근데 쉘이 변경되면서 conda 명령어를 인식을 못해서 왜그런가 했더니 환경변수를 설정해주지 않아서 그랬던 것이었다. conda 설치 경로를 확인하려고 우선 bash로 전환하고 아래 명령어를 통해 conda 경로를 확인했다.
#디폴트 쉘 bash로 설정
chsh -s /bin/bash
#디폴트 쉘 zsh로 설정
chsh -s /bin/zsh
#conda 경로 확인
which conda
conda 경로 확인하면 zshrc 파일을 열어서 맨 아래 코드를 추가한다
#환경변수 파일 즉,zshrc 파일을 열어야 한다.
vim ~/.zshrc
이때 /path/to/anaconda 부분을 본인 conda 경로로 바꿔준다.
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/path/to/anaconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "/path/to/anaconda3/etc/profile.d/conda.sh" ]; then
. "/path/to/anaconda3/etc/profile.d/conda.sh"
else
export PATH="/path/to/anaconda3/bin:$PATH"
fi
fi
unset __conda_setup
# <<< conda initialize <<<
이미 초기화된 원격의 저장소를 복제해와서 개발하는 경우, 복제 작업을 Git에서는 클론(clone)이라고 부르며 git clone서브 명령어로 구현되어있습니다. 이번 글에서는 git clone명령어의 사용법에 대해서 자세히 소개합니다.
1. 리포지토리 생성
혼자서 개발하는 경우에는 로컬에서 Git 저장소를 초기화해서 사용하면 됩니다만, 협업을 하는 경우나 인터넷에 소스 코드를 공개하는 경우 GitHub에 저장소를 만들고 이 저장소를 클론해서 작업하는 방식을 주로 사용합니다.
2. Git 저장소의 주소 파악하기
클론을 하려면 Git 저장소의 주소를 알아야합니다. 화면에서 Code 버튼을 클릭하면 Git 저장소 주소가 나타납니다. Git 저장소의 주소는 HTTPS와 SSH 방식으로 제공되며 다음과 같은 형식을 따릅니다. (메뉴에서 알 수 있지만, ZIP 압축 파일로 다운로드 받을 수도 있습니다.)
HTTPS와 SSH 형식의 GitHub 저장소 주소는 아래와 같습니다.
# HTTPS 형식
<https://github.com/[USERNAME]/[REPOSITORY_NAME].git>
# SSH 형식
git@github.com:[USERNAME]/[REPOSITORY_NAME].git
[USERNAME]은 GitHub 사용자 이름으로, [REPOSITORY_NAME]은 저장소를 생성할 때 지정한 저장소 이름이 됩니다.
3. git clone의 명령어 형식
git clone [REPO_URL] [DIR]
[REPO_URL]에는 클론해올 저장소의 주소를 지정해줍니다. [DIR]인자는 저장소를 로컬에 복제할 위치를 지정합니다. [DIR]생략 가능하며, 특별한 이유가 없다면 보통 생략합니다.
📌 파일들을 수정하다 마음에 들면 staging area에 옮겨 놓고 드디어 commit 혹은 version을 만들 준비가 되었습니다.
1. commit 실습
commit 사용해보기
version을 만들때는 commit이라는 명령어를 사용할 수 있는데, staging area에 있는 변경 사항을 git repository에 옮겨주는 역할을 한다. 아무런 옵션 없이 이용하게 되면 기본적인 template이 나오는데 보통은 Title을 작성하고 좀더 자세한 Description을 작성한 다음에 파일을 닫으면 위와 같이 나온다.
첫째 줄을 보면 main branch에 hash code의 제일 앞부분만 나온다. hash code 아이디와 Title이 표기된다. 3가지의 파일이 변경되었고 3가지 다 처음으로 만들어진 파일이란 것을 확인할 수 있다.
git history를 확인해보자
git log
commit 과 전체적인 hashcode가 나오고 누가 언제 했는지 확인할 수 있다. 그리고 Title 과 Description 이 나오는 것을 확인할 수 있다.
위와 같은 방식으로 git commit을 바로하는 경우는 거의 없고 다른 방식을 한다
echo add >> c.txt#c.txt를 수정한다
git status -s #c.txt의 상태 확인
git add. #전부다 추가
git commit -m "second commit"
git log
git commit -m 뒤에 메시지를 추가해서 간단하게 commit할 수 있다 동일하게 log 를 확인해보면 두번째 commit이 이뤄진것을 확인할 수 있다.
working directory에서 add를 사용하지 않고 전부 다 바로 commit을 해보자.
echo add >> c.txt #c.txt에 수정사항을 만들어서 working directory에 존재
git commit -am "third commit"
git status -s #git status가 깔끔해졌다
staging area와 working directory의 모든 파일들이 commit된 것을 확인해 볼 수 있다.
2. commit 팁
git directory에 있는 파일들은 사실 history에 창고다. 작업들을 버전별로 나눠서 관리할 수 있는 유용한 창고이다. 따라서 전체 어플리케이션을 만들어서 저장하면 의미가 없다. 어플리케이션을 세분화해서 기능별로 작은 단위로 만들어 나가는게 중요하다. 작은 단위로 나눠서 history에 저장하는게 중요하다.
의미있는 이름을 지정해서 저장하는게 중요하다. 예를 들어, 프로젝트를 초기화하는 commit 하나, login 서비스 모듈을 만들어서 commit하는 식으로 작은 단위로 의미있게 history를 보면 작업한 내용을 알아볼 수 있게 저장하는게 중요하다.
보통 commit의 메시지는 현재형으로 동사로 만들어진다. init, add, fix 등 commit을 할때 내가 crushing 을 고쳤다고 하면 정말 고친 내용만 포함한 commit을 만들어야지 다른 버그들 고친 것도 commit을 하면 코드를 리뷰할때도 혼동이 오지만 history를 볼때도 혼동이 온다. 따라서 commit 메시지에 맞게 해당 내용만 commit하는 것이 중요하다.
echo hello world! > a.txt
#그냥 파일 위치를 열어서 텍스트 파일을 만들어도 된다.
open .
터미널에서는 방향키 위 버튼을 누르면 이전에 작성한 명령어를 다시 활용할 수 있다. 동일한 과정으로 파일 3개를 생성하면 된다. (파일명은 a, b, c.txt)
2. git 상태 확인하기
git status
현재 working directory에 a.txt, b.txt. c.txt가 untracked된 상태로 존재한다.
3. staging area로 파일 이동
커밋할 준비가 완료 됐다고 파악하면 add 명령어를 이용해서 파일들을
staging area로 이동시킨다. 그럼 아래의 이미지와 같이 바뀐다.
git add a.txt
현재 staging area에 a.txt가 존재하고, b.txt 와 c.txt는 아직 working directory에 untracked 상태로 존재한다
나머지 파일들도 add 해준다.
#1번 방법
git add b.txt c.txt
#2번 방법 git add *.txt #존재하는 모든 txt파일을 add
> 이제 a, b, c 파일이 모두 `commit`할 준비가 되었다.
- 여기서 새로운 문자열 ellie를 a.txt 파일에 추가하게 되면 다시 git status를 확인해보자.
> 총 3가지의 파일은 commit할 준비가 되었다. 하지만 a.txt 파일이 tracked된 상태이고 modified된 것을 확인할 수 있다.
- 다시 a.txt 를 `add` 하고 확인해보자.
> a, b, c가 staging area로 옮겨진 것을 확인할 수 있다.
또한, 위의 두번째 줄을 통해서 git rm —cached 를 이용해서 staging area에서
working directory로 옮겨갈 수 있다는걸 확인할 수 있다.
- `git rm —cached`를 이용해서 모든 파일을 staging area에서 제거해보자.
```powershell
#*는 모든 파일을 의미
git rm --cached *
a, b, c 파일들이 untracked 상태로 돌아간 것을 확인할 수 있다.
마지막으로 모든 파일을 staging area로 옮긴후 a.txt를 삭제해보자.
git add *
git status # 한번 상태를 확인해본다.
rm a.txt # a.txt 삭제
ls # 디렉토리에 a.txt가 없는 것을 확인한다.
git add *
git status
a.txt를 제거하고 git status 를 확인하면 삭제된 a.txt는 디렉토리에 없었기 때문에 staging area에 추가되지 않은 것을 확인할 수 있다.