-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
1465 lines (707 loc) · 564 KB
/
search.xml
File metadata and controls
1465 lines (707 loc) · 564 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>이미 커밋된 main branch 초기화하기</title>
<link href="/2026/03/30/Etc/Initial-Main-Branch/"/>
<url>/2026/03/30/Etc/Initial-Main-Branch/</url>
<content type="html"><![CDATA[<p>개인 프로젝트라 귀찮아서 git flow 전략을 안쓰고 대충 main에 넣었더니 후회가 빗발치기 시작했다..<br>고로 main branch를 깔끔하게 초기 상태로 돌리는 방법에 대해 해보겠다.</p><h2 id="사전-세팅"><a href="#사전-세팅" class="headerlink" title="사전 세팅"></a>사전 세팅</h2><ul><li>현재 위치가 main branch인지 확인</li><li>commit이 안된 파일이 있는지 확인: 아래 명령어를 오류없이 돌리기 위해서는 모든 commit이 push된 상태일 것</li></ul><h2 id="main-branch를-초기화-시키기"><a href="#main-branch를-초기화-시키기" class="headerlink" title="main branch를 초기화 시키기"></a>main branch를 초기화 시키기</h2><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 기존 main코드를 develop으로 이전 (=코드 백업)</span><span class="token function">git</span> checkout <span class="token parameter variable">-b</span> develop<span class="token function">git</span> <span class="token function">add</span> <span class="token builtin class-name">.</span><span class="token function">git</span> commit <span class="token parameter variable">-m</span> <span class="token string">"feat: initial setup"</span><span class="token comment"># 과거 기록 없는 새 branch 생성</span><span class="token function">git</span> checkout <span class="token parameter variable">--orphan</span> main-clean<span class="token comment"># 기존 파일 전부 지워버리기 (로컬에서만 삭제됨)</span><span class="token function">git</span> <span class="token function">rm</span> <span class="token parameter variable">-rf</span> <span class="token builtin class-name">.</span><span class="token comment"># 최소한의 파일 생성 및 첫 커밋</span><span class="token comment">## develop의 .gitignore 복붙 추천</span><span class="token comment">## 아니면 merge conflict이 나는데, develop 내용을 덮어씌우면 됨</span><span class="token function">touch</span> .gitignore<span class="token function">git</span> <span class="token function">add</span> <span class="token builtin class-name">.</span><span class="token function">git</span> commit <span class="token parameter variable">-m</span> <span class="token string">"feat: initial commit"</span><span class="token comment"># main으로 강제임명 (기존 main은 강제로 덮어씌우기 당함)</span><span class="token function">git</span> branch <span class="token parameter variable">-M</span> main<span class="token comment"># 원격 저장소에 강제 푸시</span><span class="token function">git</span> push <span class="token parameter variable">-f</span> origin main<span class="token comment"># develop으로 이동</span><span class="token function">git</span> checkout develop<span class="token comment"># main을 develop으로 강제 병합 (뿌리가 달라도 허용한다는 옵션)</span><span class="token function">git</span> merge main --allow-unrelated-histories <span class="token comment"># 푸시</span><span class="token function">git</span> push origin develop</code></pre><p>이렇게 하면 두 branch는 공통된 역사를 공유하게 되며, develop에서 main으로 깔끔하게 merge available이 뜨는 것을 볼 수 있다.</p><p>다시는 git flow를 무시하지 말자 ^_^,,,</p>]]></content>
<categories>
<category> 💾 Etc </category>
</categories>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>타입폼 보고서 제출 자동화를 위한 Discord Bot 구축기</title>
<link href="/2026/03/26/Etc/Discord-Bot/"/>
<url>/2026/03/26/Etc/Discord-Bot/</url>
<content type="html"><![CDATA[<h2 id="만들게-된-이유"><a href="#만들게-된-이유" class="headerlink" title="만들게 된 이유"></a>만들게 된 이유</h2><p>최근 특정 프로그램에서 멘토링을 많이 하고 있는데, 멘토링 보고서를 제출할 때마다 타입폼 페이지에 들어가 동일한 항목을 반복 입력해야 하는 상황에 직면했다.<br>업무 난이도는 높지 않았지만.. 나레기 짱 게으르죠? 이런 간단한 것도 하기 싫죠? 애초에 사이트 들어가기 귀찮죠? 들어가더라도 맨날 똑같은 데이터 입력하기 짱짱 싫죠?</p><p>그래서 “디스코드에서 멘토링 내용을 입력하면 나머지 부분을 알아서 채워주고 보고서를 제출해주는 디스코드 봇”을 만들었다.</p><h2 id="프로젝트-목표"><a href="#프로젝트-목표" class="headerlink" title="프로젝트 목표"></a>프로젝트 목표</h2><p>이 프로젝트의 목표는 세 가지였다.</p><ol><li>운영자가 디스코드에서 빠르게 등록하고 보고할 수 있어야 했다. </li><li>등록 데이터는 일관된 형식으로 저장되고 자동 정리되어야 했다. </li><li>멘토링/수업 운영 규칙이 바뀌어도 코드 수정량을 최소화해야 했다.</li></ol><h2 id="전체-구조"><a href="#전체-구조" class="headerlink" title="전체 구조"></a>전체 구조</h2><p>현재 구조는 아래와 같다.</p><pre class="language-text" data-language="text"><code class="language-text">discord/mentoring-bot├─ main.py # 봇 시작점, cogs 자동 로딩, 로그/명령어 sync├─ cogs/│ ├─ enroll.py # /enroll 메인 로직 (질문/검증/요약/스레드/저장)│ ├─ maintenance.py # 매일 자정 만료 데이터 정리 + 저장 반영│ └─ company1/│ ├─ reporting.py # 멘토링 보고 메시지 감지 + 타입폼 자동 제출│ └─ scheduler.py # 수업 리마인드 (평일 09:30, 기간/공휴일 조건)├─ data/│ ├─ enrollment_profiles.json # 회사별 질문 순서/문구/조건/템플릿 설정│ └─ company1.json # 등록 데이터 저장소(JSON)└─ utils/ ├─ storage_manager.py # load/find/cleanup 같은 저장 유틸 ├─ git_utils.py # JSON 저장 후 Git 반영 유틸 └─ scraper.py # 타입폼 제출 자동화 유틸</code></pre><p>핵심은 코드와 운영 정책을 분리한 점이다.<br>로직은 공통 엔진으로 두고, 질문/문구/분기 조건은 프로필 파일에서 바꿀 수 있게 만들었다.</p><h2 id="왜-DB-대신-JSON을-썼는가"><a href="#왜-DB-대신-JSON을-썼는가" class="headerlink" title="왜 DB 대신 JSON을 썼는가"></a>왜 DB 대신 JSON을 썼는가</h2><p>처음부터 DB를 도입하지는 않았다. 어차피 나 혼자 쓸, 아주 자그마한 아이기 때문이었다.<br>그래서 초기에 바로 JSON 저장소를 선택했다.</p><ul><li>장점: 구축 속도가 빨랐고, 데이터 구조 확인/수정이 직관적이었다.</li><li>장점: Git 기반 백업 및 변경 추적이 쉬웠다.</li><li>단점: 동시성, 트랜잭션, 대량 데이터 처리에는 불리하다.</li></ul><p>현재 단계에서는 장점이 단점을 상회한다고 판단했다.<br>대신 파일 구조와 저장 포맷을 명확히 해두어, 추후 DB로 이관하기 쉽게 설계했다.</p><h2 id="주요-기능"><a href="#주요-기능" class="headerlink" title="주요 기능"></a>주요 기능</h2><h3 id="1-등록-자동화-enroll"><a href="#1-등록-자동화-enroll" class="headerlink" title="1) 등록 자동화 (/enroll)"></a>1) 등록 자동화 (<code>/enroll</code>)</h3><ul><li>회사 코드 기반으로 등록 플로우를 시작한다.</li><li>등록 유형(멘토링/수업), 트랙, 기수, 시작일/종료일, URL 등을 순차 입력받는다.</li><li>입력 검증(선택지/숫자/날짜 형식)을 통과한 데이터만 저장한다.</li><li>등록 완료 후 요약 메시지와 보고 스레드를 자동 생성한다.</li></ul><h3 id="2-멘토링-보고-자동-제출"><a href="#2-멘토링-보고-자동-제출" class="headerlink" title="2) 멘토링 보고 자동 제출"></a>2) 멘토링 보고 자동 제출</h3><ul><li>보고 스레드에서 지정된 양식 메시지를 감지한다.</li><li>필요한 항목을 파싱해 타입폼 제출 함수로 전달한다.</li><li>성공/실패를 이모지와 답장으로 피드백한다.</li></ul><h3 id="3-수업-알림-자동화"><a href="#3-수업-알림-자동화" class="headerlink" title="3) 수업 알림 자동화"></a>3) 수업 알림 자동화</h3><ul><li>평일 오전 9:30(KST)에만 알림을 보낸다.</li><li><code>start_date <= 오늘 <= expiry</code> 구간에서만 동작한다.</li><li>주말 및 대한민국 법정 공휴일은 스킵한다.</li></ul><h3 id="4-만료-데이터-정리"><a href="#4-만료-데이터-정리" class="headerlink" title="4) 만료 데이터 정리"></a>4) 만료 데이터 정리</h3><ul><li>매일 자정 만료 항목을 정리한다.</li><li>만료 스레드는 잠그고 아카이브 처리한다.</li><li>정리 결과를 JSON에 반영하고 저장소에 반영한다.</li></ul><h2 id="트러블슈팅-Docker-JSON-자동-커밋-푸시"><a href="#트러블슈팅-Docker-JSON-자동-커밋-푸시" class="headerlink" title="트러블슈팅: Docker + JSON 자동 커밋/푸시"></a>트러블슈팅: Docker + JSON 자동 커밋/푸시</h2><p>가장 오래 걸린 문제는 Docker 환경에서 JSON 파일을 갱신하고 Git으로 자동 반영하는 과정이었다.</p><h3 id="문제-1-저장-경로가-이중으로-생기는-문제"><a href="#문제-1-저장-경로가-이중으로-생기는-문제" class="headerlink" title="문제 1) 저장 경로가 이중으로 생기는 문제"></a>문제 1) 저장 경로가 이중으로 생기는 문제</h3><p>JSON 파일이 의도하지 않은 경로에 중복 생성되는 문제가 있었다.<br>원인은 컨테이너 마운트 경로와 실제 Git 추적 루트가 어긋났기 때문이다.<br>해결은 레포 루트를 기준으로 볼륨을 다시 잡고, 컨테이너 <code>working_dir</code>를 명확히 맞추는 방식으로 진행했다.</p><h3 id="문제-2-git-pull-rebase-충돌"><a href="#문제-2-git-pull-rebase-충돌" class="headerlink" title="문제 2) git pull --rebase 충돌"></a>문제 2) <code>git pull --rebase</code> 충돌</h3><p>서버에서 직접 파일을 수정하는 상황과 봇의 자동 저장이 겹치면서 rebase가 자주 실패했다.<br>해결은 <code>--autostash</code> 적용과 저장 타이밍 정리였다.<br>또한 Git 루트를 탐색해 <code>git -C <repo_root></code> 형태로 명령을 고정해 경로 오류를 줄였다.</p><h3 id="문제-3-Docker의-dubious-ownership"><a href="#문제-3-Docker의-dubious-ownership" class="headerlink" title="문제 3) Docker의 dubious ownership"></a>문제 3) Docker의 dubious ownership</h3><p>마운트된 레포 소유권이 컨테이너 유저와 달라 Git 보안 검증에 걸렸다.<br>해결은 컨테이너에서 safe directory를 명시하는 방식으로 처리했다.</p><p>이 과정을 통해 “코드가 맞아도 배포 환경이 다르면 자동화가 실패할 수 있다”는 점을 크게 체감했다.</p><h2 id="결과"><a href="#결과" class="headerlink" title="결과"></a>결과</h2><img src="/2026/03/26/Etc/Discord-Bot/discord-bot-result.png" class="" title="결과물"><p>대충 이런식으로 적으면 알아서 멘토링 팀을 관리해주고 일정 리마인드를 해준다. 또한 보고서 내용을 간략히 입력하면 알아서 타입폼의 데이터를 세팅 후 제출해준다!</p><h2 id="이번-작업에서-얻은-결론"><a href="#이번-작업에서-얻은-결론" class="headerlink" title="이번 작업에서 얻은 결론"></a>이번 작업에서 얻은 결론</h2><p>작은 불편을 자동화하는 작업은 생각보다 가치가 컸다.<br>반복 입력을 줄인 것 자체보다, 운영 절차를 코드로 표준화한 효과가 더 컸기 때문이다.<br>그리고 기능을 늘릴수록 “하드코딩보다 설정 기반 구조가 장기적으로 유리하다”는 점이 명확해졌다.</p><h2 id="다음-확장-계획"><a href="#다음-확장-계획" class="headerlink" title="다음 확장 계획"></a>다음 확장 계획</h2><p>다음 단계에서는 아래 항목을 확장할 계획이다.</p><ul><li>회사별 타입폼 매핑 규칙 완전 분리</li><li>공휴일/예외일 커스텀 캘린더 지원</li><li>관리자용 상태 조회 커맨드(등록 현황, 만료 예정, 알림 대상)</li><li>JSON에서 DB로 무중단 이관 가능한 저장 계층 추상화</li><li>AI API를 연결하여 더 정갈한 보고서 생성</li></ul><p>아직은 초기라서 이정도로 생각하고 있지만, 나중에 쓰다보면 더 생기지 않을까 싶다,,,</p>]]></content>
<categories>
<category> 💾 Etc </category>
</categories>
<tags>
<tag> Bot </tag>
<tag> Discord </tag>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Terraform + Organizations로 Cross-Account DNS 관리하기</title>
<link href="/2026/03/17/Cloud/CrossAccountDNS/"/>
<url>/2026/03/17/Cloud/CrossAccountDNS/</url>
<content type="html"><![CDATA[<blockquote><p>최근 회사에서 멀티 환경 클라우드 플랫폼을 구성하고 있는데 블로그 글로 남기면 좋을 것 같아서 작성해본다.</p><p>회사에서는 주로 AWS를 쓰기 때문에, 본 글은 AWS를 예시로 작성을 해봤다.</p></blockquote><hr><h2 id="1-DNS관리-보통은-어떻게-하는가"><a href="#1-DNS관리-보통은-어떻게-하는가" class="headerlink" title="1. DNS관리, 보통은 어떻게 하는가"></a>1. DNS관리, 보통은 어떻게 하는가</h2><p>멀티 환경 클라우드 플랫폼을 구성할 때, 자주 부딪히는 문제가 있다. 바로 <strong>DNS관리</strong>이다.</p><p>보통은 각 환경(dev, qa, prod)별로 서로 다른 AWS 계정이 있다. 그럼 ‘그냥 각 환경에서 Route53 파서 Hosted Zone 등록하면 되지 않나?’라고 생각이 든다면… 당신은 클라우드 개발자로서 아직 하수인 것,,,</p><p>클라우드 엔지니어는 클라우드에서 개발하는 사람이지만, 비즈니스 관점에서 보자면 <strong>돈을 잡아먹는 하마 그 자체</strong>이다. 남의 컴퓨터 빌려쓰는건데 땅파서 돈이 나오나 그냥 회삿돈 쓰는거지 뭐 ㅎㅎ;;</p><p>그렇다보니 클라우드 엔지니어는 클라우드를 잘 활용하는 것 뿐만 아니라 <strong>돈을 얼마나 아끼는, 효율적으로 아키텍쳐를 설계하느냐</strong>에 따라 실력이 나뉜다고 할 수 있다.</p><br><p>각설하고, 그럼 Hosted Zone을 각 계정에서 판다는게 왜 하수이냐? 라고 물어본다면 이렇게 답하면 된다. <strong>만들 때마다 비용이 나가기 때문이다</strong></p><p>각 클라우드 업체마다 비용은 다르지만, 일단 Hosted Zone을 생성하면 최소 $0.2-0.5가 청구된다. 이게 B2B로 계속 account를 만들어서 세팅해야 한다면? account 개수만큼 이 비용이 나가는 것이다. 물론 작다면 작은 금액이지만, 클라우드 엔지니어는 이 비용조차 줄여야 비로소 실력이 있다는 소리를 들을 수 있다.</p><p>그럼 실무에서는 이걸 어떻게 관리하냐, 바로 <strong>DNS전용 Account를 따로 생성해서 여기서 모든 dns를 관리</strong>한다. 그리고 각 account에서 이 정보를 가져와서 사용하면, 아무리 account가 늘더라도 딱 하나의 Hosted Zone비용을 대기만 하면 된다.</p><pre class="language-none"><code class="language-none">DNS Account└── Route53: example.com ├── cloud.dev.example.com ├── cloud.prod.example.com └── cloud.qa.example.com</code></pre><p>여기서 문제가 하나 생긴다.</p><p><strong>각 환경은 서로 다른 AWS 계정인데, 어떻게 공용 DNS Account의 Route53에 자동으로 접근해서 레코드를 생성할 수 있을까?</strong></p><p>즉, 우리는 <strong>Cross-Account DNS 접근 문제</strong>를 해결해야 한다.</p><hr><h2 id="2-문제-계정별-권한-관리의-한계"><a href="#2-문제-계정별-권한-관리의-한계" class="headerlink" title="2. 문제: 계정별 권한 관리의 한계"></a>2. 문제: 계정별 권한 관리의 한계</h2><p>가장 단순한 방법은 DNS Account에 있는 IAM Role(<code>TerraformDNSRole</code>)의 trust policy에 각 환경 계정을 하나씩 추가하는 것이다.</p><pre class="language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"Principal"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"AWS"</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"arn:aws:iam::111111111111:root"</span><span class="token punctuation">,</span> <span class="token string">"arn:aws:iam::222222222222:root"</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>이 방식은 처음에는 잘 동작한다.<br>하지만 시간이 지나면 문제가 생긴다:</p><ul><li>새로운 환경 (ex. staging) 추가</li><li>고객별 전용 account 생성</li><li>테스트용 account 생성</li></ul><p>그때마다:</p><ol><li>DNS Account에 로그인</li><li>IAM Role 수정</li><li>account ID 추가</li></ol><p>이 과정을 반복해야 한다.<br>즉 자동화는 개뿔 초반 세팅에서 무조건 account에 접속해서 일일이 Role과 policy를 생성해야 한다는 것이다.</p><hr><h2 id="3-해결-AWS-Organizations-기반-접근-제어"><a href="#3-해결-AWS-Organizations-기반-접근-제어" class="headerlink" title="3. 해결: AWS Organizations 기반 접근 제어"></a>3. 해결: AWS Organizations 기반 접근 제어</h2><p>이 문제를 깔끔하게 해결하는 방법이 있다.</p><p>바로 <strong>AWS Organizations</strong>를 활용하는 것이다.</p><p>개별 계정을 나열하는 대신, 아래처럼 설정한다:</p><pre class="language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span><span class="token punctuation">,</span> <span class="token property">"Principal"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"AWS"</span><span class="token operator">:</span> <span class="token string">"*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"Action"</span><span class="token operator">:</span> <span class="token string">"sts:AssumeRole"</span><span class="token punctuation">,</span> <span class="token property">"Condition"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"StringEquals"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"aws:PrincipalOrgID"</span><span class="token operator">:</span> <span class="token string">"o-xxxxxxxxxx"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>이 설정의 의미는:</p><ul><li><code>"AWS": "*"</code> → 누구나 접근 가능해 보이지만</li><li><code>aws:PrincipalOrgID</code> 조건 → <strong>우리 Organization에 속한 계정만 허용</strong></li></ul><p>결과적으로:</p><ul><li>새로운 AWS 계정을 만들어도</li><li>자동으로 접근 가능</li><li>추가 설정 필요 없음</li></ul><hr><h2 id="4-Terraform에서-Cross-Account-DNS-사용하기"><a href="#4-Terraform에서-Cross-Account-DNS-사용하기" class="headerlink" title="4. Terraform에서 Cross-Account DNS 사용하기"></a>4. Terraform에서 Cross-Account DNS 사용하기</h2><p>이제 Terraform에서 이 Role을 어떻게 사용할까?</p><p>각 환경의 <code>providers.tf</code>에 다음과 같이 설정한다:</p><pre class="language-hcl" data-language="hcl"><code class="language-hcl"><span class="token keyword">provider<span class="token type variable"> "aws" </span></span><span class="token punctuation">{</span> <span class="token property">alias</span> <span class="token punctuation">=</span> <span class="token string">"dns"</span> <span class="token property">region</span> <span class="token punctuation">=</span> <span class="token string">"us-east-1"</span> <span class="token keyword">assume_role</span> <span class="token punctuation">{</span> <span class="token property">role_arn</span> <span class="token punctuation">=</span> <span class="token string">"arn:aws:iam::123456789010:role/TerraformDNSRole"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>그리고 Route53 리소스에서 해당 provider를 사용한다:</p><pre class="language-hcl" data-language="hcl"><code class="language-hcl"><span class="token keyword">resource <span class="token type variable">"aws_route53_record"</span></span> <span class="token string">"program"</span> <span class="token punctuation">{</span> <span class="token property">provider</span> <span class="token punctuation">=</span> aws.dns <span class="token property">zone_id</span> <span class="token punctuation">=</span> var.route53_hosted_zone_id <span class="token property">name</span> <span class="token punctuation">=</span> local.domain_name <span class="token property">type</span> <span class="token punctuation">=</span> <span class="token string">"A"</span><span class="token punctuation">}</span></code></pre><p>이렇게 하면:</p><ul><li>Terraform은 현재 account에서 실행되지만</li><li>내부적으로 DNS Account로 Role assume 후</li><li>Hosted Zone에 레코드를 생성한다</li></ul><hr><h2 id="5-그래서-DNS-Account에는-뭘-해야-할까"><a href="#5-그래서-DNS-Account에는-뭘-해야-할까" class="headerlink" title="5. 그래서 DNS Account에는 뭘 해야 할까"></a>5. 그래서 DNS Account에는 뭘 해야 할까</h2><p>여기까지 보면 이런 생각이 든다.</p><blockquote><p>“그래서 DNS Account에는 뭐 만들어야 하는거지?”</p></blockquote><p>결론부터 말하면 간단하다.</p><p>👉 <strong>IAM Role 하나만 제대로 만들어두면 끝이다</strong></p><p>이 Role 이름이 바로 <code>TerraformDNSRole</code>이다.</p><p>이 Role에는 두 가지가 필요하다:</p><h3 id="1-Trust-Policy"><a href="#1-Trust-Policy" class="headerlink" title="1) Trust Policy"></a>1) Trust Policy</h3><pre class="language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span><span class="token punctuation">,</span> <span class="token property">"Principal"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"AWS"</span><span class="token operator">:</span> <span class="token string">"*"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"Action"</span><span class="token operator">:</span> <span class="token string">"sts:AssumeRole"</span><span class="token punctuation">,</span> <span class="token property">"Condition"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"StringEquals"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"aws:PrincipalOrgID"</span><span class="token operator">:</span> <span class="token string">"o-xxxxxxxxxx"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h3 id="2-Permissions-Policy"><a href="#2-Permissions-Policy" class="headerlink" title="2) Permissions Policy"></a>2) Permissions Policy</h3><pre class="language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"Effect"</span><span class="token operator">:</span> <span class="token string">"Allow"</span><span class="token punctuation">,</span> <span class="token property">"Action"</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"route53:ChangeResourceRecordSets"</span><span class="token punctuation">,</span> <span class="token string">"route53:GetHostedZone"</span><span class="token punctuation">,</span> <span class="token string">"route53:ListResourceRecordSets"</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token property">"Resource"</span><span class="token operator">:</span> <span class="token string">"arn:aws:route53:::hostedzone/ZXXXXXXXXXXXXX"</span><span class="token punctuation">}</span></code></pre><p>이후 account가 몇 개가 늘어나든 DNS Account를 다시 건드릴 필요가 없다!</p><hr><h2 id="6-전체-흐름-한-번에-보기"><a href="#6-전체-흐름-한-번에-보기" class="headerlink" title="6. 전체 흐름 한 번에 보기"></a>6. 전체 흐름 한 번에 보기</h2><pre class="language-none"><code class="language-none">DNS Account└── Route53 (example.com) ↑ │ (assume role) │Dev Account ── Terraform applyQA Account ── Terraform applyProd Account ── Terraform apply</code></pre>]]></content>
<categories>
<category> ☁️ Cloud </category>
</categories>
<tags>
<tag> Architecture </tag>
<tag> Cloud </tag>
<tag> Terraform </tag>
</tags>
</entry>
<entry>
<title>Proxmox와 Tailscale로 구축하는 Zero-Trust 홈 인프라 (feat. WOL 원격 대시보드 만들기)</title>
<link href="/2026/03/15/NS/ZeroTrustHomeInfra/"/>
<url>/2026/03/15/NS/ZeroTrustHomeInfra/</url>
<content type="html"><![CDATA[<h2 id="1-개요"><a href="#1-개요" class="headerlink" title="1. 개요"></a>1. 개요</h2><a href="/2026/02/28/Tips/UbuntuServer-Setting/" title="안쓰는 노트북을 홈서버로 개조해보자 (feat. Windows를 밀고 Ubuntu Server 세팅하기)">안쓰는 노트북을 홈서버로 개조해보자 (feat. Windows를 밀고 Ubuntu Server 세팅하기)</a><p>이전 글에서 노트북으로 서버를 만들었다고 했는데, 흠 집안을 뒤져보니 안쓰는 PC가 있네? 그래서 제대로 된 서버를 만들어봤다.</p><p>글을 읽어보면 이렇게까지 할 필요가 있냐 싶기도 했지만… 분명 동생이 공부하다보면 높은 확률로 사고를 칠 것 같고, 괜히 고친답시고 이것저것 건들였다가 서버가 아예 터질 수도 있겠다는 생각이 들어 그냥 처음부터 고생길을 선택했다.<br>이 정도로 설계해줬음 터져도 자기꺼만 터지겠지 ㅎㅎ 터지면 혼자 고쳐보렴ㅎㅎㅎㅎ <del>미안하다 나에겐 너의 신뢰도가 매우 바닥이구나</del></p><p>포트폴리오로 쓰기엔 애매해서 블로그에 있어보이게 남기는거니 혹시나 홈서버 세팅에 관심있어서 들어오신 분들은 각 항목의 <strong>비하인드 /쉬운 설명</strong> 파트만 보심 됩니다.</p><hr><h2 id="2-시스템-아키텍처-System-Architecture"><a href="#2-시스템-아키텍처-System-Architecture" class="headerlink" title="2. 시스템 아키텍처 (System Architecture)"></a>2. 시스템 아키텍처 (System Architecture)</h2><p>보통 홈 서버를 운영하다 보면 두 가지 큰 고민에 직면하게 된다.</p><ol><li><strong>보안:</strong> 외부 접속을 위해 포트 포워딩을 하는 순간, 전 세계 봇들의 스캔 대상이 됨</li><li><strong>효율:</strong> 24시간 가동은 전기세와 하드웨어 수명에 부담이 됨</li></ol><p>이를 해결하기 위해 <strong>Type-1 하이퍼바이저(Proxmox)</strong> 와 <strong>Mesh VPN(Tailscale)</strong> 을 결합하여, 포트 노출 없는 Zero Trust 환경과 원격 전원 제어 시스템을 구축했다.</p><pre class="language-none"><code class="language-none">[ Tailscale Mesh VPN (Tailnet) ] │ ┌──────┴──────┐ ▼ ▼┌──────────────┐ ┌──────────────────────────────────────────┐│ [ Laptop ] │ │ [ Proxmox Main Server ] ││ (auto) │ │ │├──────────────┤ ├──────────────────────────────────────────┤│ WOL Dash │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ (Flask) │ │ │ AI VM │ │ Dev VM │ │ Win VM │ ││ │ │ │ (GPU) │ │ │ │ │ ││ │ │ └──────────┘ └──────────┘ └──────────┘ │├──────────────┤ ├──────────────────────────────────────────┤│ Tailscale │ │ Tailscale Serve (home-proxmox) ││ Serve │ │ ││ (auto/wol) │ │ │└──────────────┘ └──────────────────────────────────────────┘</code></pre><p>인프라는 크게 가상화 계층과 네트워크 계층으로 설계해봤다.</p><ul><li><p><strong>가상화 계층 - Virtualization (Proxmox):</strong></p><ul><li>Bare-metal 환경에 Type-1 하이퍼바이저인 Proxmox를 구성하여 자원 효율성 및 하드웨어 추상화 달성</li><li><strong>핵심 포인트:</strong> AI 서버(GPU Passthrough), Dev 서버, Windows VM 등 목적별 VM 격리를 통해 운영 안정성을 확보하고, 스냅샷 기반의 빠른 복구 환경 구축</li></ul></li><li><p><strong>네트워크 계층 - Networking (Tailscale):</strong></p><ul><li>공유기 Port Forwarding을 완전히 배제하고 Tailscale 기반의 Mesh VPN으로 <strong>제로 트러스트(Zero Trust)</strong> 접속 환경 구현</li><li><strong>핵심 포인트:</strong> Subnet Router 설정을 통해 외부에서도 별도 클라이언트 설치 없이 내부 대역에 직접 접근 가능하도록 라우팅 최적화</li></ul></li><li><p><strong>제어 및 자동화 계층 - Control Plane (Laptop/Flask):</strong></p><ul><li>메인 서버가 다운된 상태에서도 상시 가동되는 저전력 노트북을 <strong>Control Plane</strong>으로 별도 분리하여 물리적 가용성 확보</li><li><strong>핵심 포인트(OOB Management):</strong> 고가의 서버 관리 솔루션(IPMI) 대신 L2(WOL) 및 L7(SSH) 프로토콜을 결합하여 가성비 높은 원격 전원 제어 및 상태 모니터링 자동화</li></ul></li></ul><blockquote><h4 id="비하인드-쉬운-설명"><a href="#비하인드-쉬운-설명" class="headerlink" title="비하인드 / 쉬운 설명"></a>비하인드 / 쉬운 설명</h4><h5 id="1️⃣-Proxmox-및-Tailscale-선택-배경"><a href="#1️⃣-Proxmox-및-Tailscale-선택-배경" class="headerlink" title="1️⃣ Proxmox 및 Tailscale 선택 배경"></a>1️⃣ Proxmox 및 Tailscale 선택 배경</h5><ol><li><strong>외국 나가서도 내맘대로 컴퓨터 켰다껐다 쓰고싶은데? 원격으로 컴퓨터 화면도 보고싶고</strong><ul><li>홈서버라서 VM을 많이 만들지는 않을 것 같은데, 그 중 중소규모 회사에서 Proxmox라는걸 꽤 사용하는 것 같으니 적용해보기로 결정</li><li>컴퓨터가 꺼지면 proxmox 서버자체가 안됨. 즉 컴퓨터 전원을 켜고 끄는 프로그램을 proxmox에서 돌리면 전혀 안됨 -> 아예 다른 곳, 그러니까 노트북 ubuntu server에 proxmox를 WOL할 수 있도록 간단한 프로그램을 만들고 24시간 돌리자. 그러면 proxmox가 올려진 컴퓨터는 꺼져있지만, 노트북은 24시간 풀가동이니 프로그램이 잘 작동할 것이다</li></ul></li><li><strong>괜히 포트 열었다가 해커들이 침입하면?</strong> -> Tailscale로 권한 있는 사람만 서버에 접속할 수 있도록 하자</li><li><strong>만약 VM을 더 만들게 된다면?</strong> -> proxmox에서 필요할 때 만들면 되겠지</li></ol><h5 id="2️⃣-AI-VM과-Dev-VM을-나눈-이유"><a href="#2️⃣-AI-VM과-Dev-VM을-나눈-이유" class="headerlink" title="2️⃣ AI VM과 Dev VM을 나눈 이유"></a>2️⃣ AI VM과 Dev VM을 나눈 이유</h5><p>일단 이 둘은 모두 <strong>Ubuntu Server</strong>로 되어있다. 즉 기본 베이스는 똑같은 시스템이다. 원래는 하나의 VM으로 만들어 user를 각각 만들어 관리를 하려고 했는데, <strong>GPU</strong>가 마음에 걸렸다. 서버 돌리기 전에 컴퓨터를 뜯어보니까 그래픽카드가 있더라고,, 이왕 있는거 써먹어야 하지 않겠나,,</p><ul><li>보통 AI모델을 돌리는 순간, VRAM(GPU 메모리)가 꽉 차버리게 된다. 이 때 같은 곳에서 개발 서버를 돌리면 가뜩이나 부족한 자원을 나눠쓰느라 둘 다 버벅거리거나, 한 쪽이 커널 패닉으로 죽어버릴 것 같다는 생각이 들었다.</li><li>또한 AI용 라이브러리와 개발용 라이브러리가 한 곳에 있으면 업데이트 하나 잘못 했다가 전체 환경이 깨져버리는 대참사가 일어날거라 생각했다.</li></ul><p>그래서 <strong>AI VM</strong>과 <strong>Dev VM</strong>으로 각각 Ubuntu server VM을 만들어 세팅을 해놨다.</p><ul><li>AI VM: Proxmox에서 물리 GPU를 Passthrough하여 이 VM에만 할당했다. 이 경우, 다른 VM은 GPU가 있는지조차 모르기 때문에 최적의 성능을 뽑아낼 수 있을거라 생각했다.</li><li>Dev VM: CPU와 RAM 위주로 할당하여, 코드를 돌리고 프로그램을 돌리는데 집중하도록 만들었다. AI모델이 아무리 무거워도 개발 서버는 쾌적하겠지,,</li></ul><h5 id="3️⃣-Windows-VM이-있는-이유"><a href="#3️⃣-Windows-VM이-있는-이유" class="headerlink" title="3️⃣ Windows VM이 있는 이유"></a>3️⃣ Windows VM이 있는 이유</h5><p>종종 정부사이트를 들락거리는데, 윈도우 최적화다보니 맥북에서 잘 돌아가지 않는 경우가 있음; 그래서 공문서 처리가 필요할 경우를 대비해 만들어놨다. 정부는 각성하라 21세기 AI가 판치는 시대에 이게 말이냐 방귀냐</p></blockquote><hr><h2 id="3-주요-구현-기술-Tech-Stack"><a href="#3-주요-구현-기술-Tech-Stack" class="headerlink" title="3. 주요 구현 기술 (Tech Stack)"></a>3. 주요 구현 기술 (Tech Stack)</h2><h3 id="3-1-Zero-Trust-Mesh-VPN-L7-Routing"><a href="#3-1-Zero-Trust-Mesh-VPN-L7-Routing" class="headerlink" title="3.1 Zero Trust Mesh VPN & L7 Routing"></a>3.1 Zero Trust Mesh VPN & L7 Routing</h3><p>단순한 포트 포워딩 방식의 외부 접속을 배제하고, 신원 기반의 안전한 <strong>인입 경로(Ingress)</strong> 를 설계</p><ul><li><strong>Mesh VPN (Tailscale):</strong> WireGuard 프로토콜 기반의 가상 네트워크를 구축하여, 공인 IP 노출 없이 <strong>Full-mesh 터널링</strong>을 구현 -> 전 세계 어디서든 내부망에 안전하게 인입할 수 있는 환경을 조성</li><li><strong>L7 Reverse Proxy (Tailscale Serve):</strong> 각 물리 노드에서 <code>Tailscale Serve</code>를 독립적으로 운용하여 <strong>도메인 기반 경로 매핑(Context Path Routing)</strong> 을 적용<ul><li>내부의 비표준 포트(<code>:5000</code>, <code>:8006</code>)를 은닉하고 HTTPS 표준 포트(443)로 통신을 단일화하여 접근성과 보안성을 동시에 확보</li></ul></li></ul><h3 id="3-2-Out-of-Band-Management-OOB-유사-구현"><a href="#3-2-Out-of-Band-Management-OOB-유사-구현" class="headerlink" title="3.2 Out-of-Band Management (OOB) 유사 구현"></a>3.2 Out-of-Band Management (OOB) 유사 구현</h3><p>물리 서버의 라이프사이클(부팅/종료)을 원격에서 제어하기 위해 <strong>L2(Data Link)</strong> 와 <strong>L7(Application)</strong> 계층의 프로토콜을 조합</p><ul><li><strong>L2 Layer (WOL):</strong> 관리 노드(Laptop)에서 메인 서버의 MAC 주소를 타겟으로 <strong>UDP 브로드캐스트 매직 패킷</strong>을 송출하여 하드웨어 부팅을 트리거</li><li><strong>L7 Layer (SSH/Paramiko):</strong> 시스템 종료 시에는 Python의 <code>Paramiko</code> 라이브러리를 통해 SSH 세션을 수립하고 <code>shutdown now</code> 명령을 전달: 단순 전원 차단이 아닌 <strong>Graceful Shutdown</strong>을 보장하여 가상 머신(VM) 및 데이터의 무결성을 유지</li><li><strong>State Monitoring:</strong> Python <code>socket</code> 라이브러리를 활용해 서버의 특정 서비스 포트 활성 여부를 주기적으로 스캔하여, 대시보드에 실시간 가동 상태(Online/Offline)를 리포팅</li></ul><img src="/2026/03/15/NS/ZeroTrustHomeInfra/1.png" class="" title="WOL"><img src="/2026/03/15/NS/ZeroTrustHomeInfra/2.png" class="" title="repo README.md"><h3 id="3-3-하드웨어-가용성-최적화-Persistence"><a href="#3-3-하드웨어-가용성-최적화-Persistence" class="headerlink" title="3.3 하드웨어 가용성 최적화 (Persistence)"></a>3.3 하드웨어 가용성 최적화 (Persistence)</h3><p>구형 하드웨어를 서버로 재활용할 때 발생할 수 있는 가용성 저하 문제를 물리 계층에서 해결</p><ul><li><strong>CMOS 배터리(CR2032) 교체:</strong> 방전된 배터리로 인해 전원 차단 시 BIOS 설정(WOL, Power Loss Recovery)이 초기화되는 문제를 방지하여 인프라의 <strong>지속성(Persistence)</strong> 을 확보</li><li><strong>Workload Isolation:</strong> GPU 자원을 과하게 소모하는 AI VM과 서비스 가동률이 중요한 Dev VM을 분리하고, <strong>GPU Passthrough</strong>를 적용하여 리소스 간섭 없는 고성능 연산 환경을 구축</li></ul><blockquote><h4 id="비하인드-쉬운-설명-1"><a href="#비하인드-쉬운-설명-1" class="headerlink" title="비하인드 / 쉬운 설명"></a>비하인드 / 쉬운 설명</h4><h5 id="1️⃣-CMOS-배터리-교체-사건"><a href="#1️⃣-CMOS-배터리-교체-사건" class="headerlink" title="1️⃣ CMOS 배터리 교체 사건"></a>1️⃣ CMOS 배터리 교체 사건</h5><ul><li>원래 컴퓨터는 ASUS기반, 윈도우 10이 깔려 있었다. 다 갈아엎고 proxmox로 하려면 BIOS에서 UEFI로 설정을 변경하라네? 인터넷에 검색해보니 Asus에서는 F2나 DEL버튼을 누르면 BIOS에 접속할 수 있다고 나와있어서 했다. 그런데 문제가 발생했다.</li><li>문제는 바로 <strong>BIOS 화면이 전혀 나오지 않는다</strong> 는 것이었다. 안보이는 화면에서 키를 연타해보기, 윈도우에서 직접 UEFI 펌웨어 설정에 들어가기, 전원 케이블을 뽑고 잔류 전원을 다 빼고 재시동을 거는 등 여러 실험을 해봤다. 내린 결론으로는 <strong>Windows 자체는 괜찮았지만 그래픽 카드에서 문제가 발생한 것</strong>이였다. 왜냐면 조금 기다리면 윈도우는 잘 떴기 때문이다.</li><li><code>1차 시도</code><ul><li>인터넷에 검색해보니 <a href="https://www.nvidia.com/en-us/drivers/nv-uefi-update-x64/">Nvidia Graphics Firmware Update Tool for DisplayPort 1.3 and 1.4 Displays</a>에서 파일을 다운받아 드라이버를 업데이트 한다면 해결된다는 얘기가 있었다. 많은 사람들이 댓글로 감사합니다 ㅠㅠ 를 적은 것을 보고, 나도 희망을 품고 업데이트를 해봤다.</li><li>하지만 안타깝게도 내 GPU는 최신버전을 갖고 있었다. 그렇게 1차 시도는 실패로 돌아가버렸다.</li></ul></li><li><code>2차 시도</code><ul><li>어쩔 수 없이 AI한테도 물어보고 인터넷에도 검색해봤는데, <strong>CMOS를 초기화</strong>하면 된다고 하더라. 나는 소프트웨어만 주구장창 팠던 사람이지 컴퓨터 하드웨어는 1도 모르는 사람인데 뭘 어쩌라는ㄱ… 라고 투덜대기 전에 그냥 냅다 해봤다. <del>되거나 부숴지거나 결과는 두 가지 밖에 없지</del></li><li>어찌저찌 드라이버로 나사를 풀었더니, 듣기로는 CMOS가 그 동전건전지라는데 안보이네? 또 AI와 인터넷에 검색해봤더니 <strong>GPU 뒤에 숨겨져 있을 가능성이 있다</strong>라고 한다. 그래서 GPU를 분해했다. 부품 자체를 처음 만져보는거라 이게 맞나 싶어 + 부숴질까봐 유튜브를 보면서 해결했다.</li><li>GPU를 뜯으니 그 동전이 있었는데, 유튜브를 보면서 해도 동전이 전혀 나오지 않았다… 그렇게 또 나는 인터넷 유목민이 되어 AI와 인터넷 검색을 계속 했고, <strong>CLRTC 또는 JBAT1 또는 CLR_CMOS 라고 적힌 점퍼에 금속 드라이버로 5-10초 정도 금속을 접촉하면 된다</strong>는 정보를 입수했다. CMOS와 한 2cm? 꽤 가까운 곳에 해당 점퍼를 찾을 수 있었다. 웃긴게 GPU 분해 안하고도 밖에서 잘 보이더라ㅋㅋㅋㅠㅠㅠㅠ 괜한 고생을 한거였다. (혹시 초기화할 사람이라면 GPU분해 전에 한 번 보드를 쭉 흝어보기 바란다. 나처럼 고생하지 마세요…) 그렇게 또 희망을 품고 금속 접촉을 한 뒤, 잔류전원을 확실하게 빼주고 전원을 연결하여 BIOS화면이 나오길 기대했다. 그런데 큰일났다. <strong>컴퓨터 화면 자체가 아예 켜지지 않는다는 것</strong>이었다. 잘됐던 윈도우도 그렇게 암흑으로 사라졌다…</li></ul></li><li><code>3차 시도</code><ul><li>분명 조심해서 다뤘는데 안된다? 이건 말도 안된다 윈도우라도 살려야 한다는 명목 하에 다시 컴퓨터를 분해하여 RAM을 다시 뺐다 꼽고, 청소를 싹 해줬다. 응 그래도 전혀 안나와~</li><li>하지만 컴퓨터 신호가 잘 간다는건 볼 수 있었다. <strong>연결했던 키보드에는 불이 잘 들어왔기 때문이다</strong>.</li><li>2차 시도에서 AI가 <strong>CMOS라는건 모든 컴퓨터 부품에 들어간다며, 못찾는건 말이 안된다</strong>라며 내게 고나리를 했었는데, 이 배터리를 교체하면 되지 않을까? 해서 바로 동전 건전지를 구매했다. <strong>ECR2032</strong>로 샀고, 다시 컴퓨터 -> GPU를 분해하고 CMOS 배터리를 갈아끼웠다. (이때 즈음 부터는 분해에 자신감이 넘치기 시작함) 그렇게 전원을 켰더니 드디어 BIOS에 진입할 수 있게 되었다 😂</li></ul></li></ul><h5 id="2️⃣-WOL-사용-OOB-유사-구현"><a href="#2️⃣-WOL-사용-OOB-유사-구현" class="headerlink" title="2️⃣ WOL 사용: OOB 유사 구현"></a>2️⃣ WOL 사용: OOB 유사 구현</h5><ul><li>원래는 집에 미리 구비해둔 스마트플러그로 전원을 키면 자동으로 컴퓨터가 켜지도록 세팅을 했다. 그럼 동생한테 스마트플러그 권한을 줘야하는데, 어차피 Tailscale로 VPN 해놨으니, 내가 웹페이지만 조금 만들어서 노트북 서버로 24시간 가동하면 되는거 아닌가?해서 간단하게 프로그래밍을 해봤다.</li><li>켜기: <code>wakeonlan</code>의 <code>send_magic_packet</code>을 이용해 컴퓨터 맥주소로 쏴줬다. 버튼 누르니까 컴퓨터 전원이 켜져서 신기하더라</li><li>끄기: 원래는 끄기 버튼은 구현을 안하려고 했다. 어차피 proxmox 접속해서 사용하려는 VM을 실행해야 할텐데? 그러면 VM를 정리하고 proxmox까지 잘 끄지 않을까? 라고 생각했기 때문이다. 하지만 사람 일은 모르는 것. 혹시나 VM만 켜놓고 proxmox는 안끌 수도 있을 것 같아서, 끄기 신호를 보내면 <strong>SSH로 로그인 후 <code>shutdown now</code> 명령어를 보내도록</strong> 설정했다.</li></ul></blockquote><hr><h2 id="4-트러블슈팅-설계와-현실의-간극-해결"><a href="#4-트러블슈팅-설계와-현실의-간극-해결" class="headerlink" title="4. 트러블슈팅: 설계와 현실의 간극 해결"></a>4. 트러블슈팅: 설계와 현실의 간극 해결</h2><h3 id="4-1-L7-리버스-프록시-경로-매핑-Flask-404-에러"><a href="#4-1-L7-리버스-프록시-경로-매핑-Flask-404-에러" class="headerlink" title="4.1 L7 리버스 프록시 경로 매핑 (Flask 404 에러)"></a>4.1 L7 리버스 프록시 경로 매핑 (Flask 404 에러)</h3><ul><li><strong>문제 상황:</strong> <code>Tailscale Serve</code>로 <code>/wol</code> 경로를 Flask 앱에 매핑했으나, 대시보드 UI만 뜨고 상태 확인 API(<code>api/status</code>)는 404 에러와 함께 무한 로딩됨</li><li><strong>원인 분석:</strong><ul><li>Reverse Proxy의 <strong>Path Stripping</strong> 메커니즘과 브라우저의 <strong>Context Path</strong> 인식 불일치가 원인<br>브라우저는 <code>/wol/api/...</code>를 호출하지만, 프록시 설정에 따라 Flask 앱은 이를 처리할 엔드포인트를 찾지 못했음</li></ul></li><li><strong>해결:</strong><ul><li>Tailscale Serve 설정을 <code>--set-path /wol/</code>로 명시하여 경로 구분을 명확히 함</li><li>클라이언트 코드(<code>fetch</code>)의 주소를 절대 경로(<code>/wol/api/...</code>)로 강제하여 프록시 환경에서도 일관된 라우팅을 보장하도록 수정</li></ul></li></ul><h3 id="4-2-vGPU-Shared-GPU-할당-실패-및-Passthrough-선회"><a href="#4-2-vGPU-Shared-GPU-할당-실패-및-Passthrough-선회" class="headerlink" title="4.2 vGPU(Shared GPU) 할당 실패 및 Passthrough 선회"></a>4.2 vGPU(Shared GPU) 할당 실패 및 Passthrough 선회</h3><ul><li><strong>문제 상황:</strong> 자원 효율성을 위해 하나의 GPU를 여러 VM이 공유하는 <strong>vGPU(Shared GPU)</strong> 설정을 시도했으나, Proxmox GUI에서 할당 시 Host 커널이 Crash 되는 현상 발생</li><li><strong>원인 분석:</strong> 사용 중인 <strong>Nvidia GeForce GTX 1050Ti</strong>는 엔터프라이즈급 라이선스나 특수 드라이버 패치 없이는 하드웨어 차원에서 vGPU 기능을 제한함 -> 이를 강제로 할당하려다 보니 자원 충돌이 발생</li><li><strong>해결:</strong> <strong>인프라의 최우선 가치는 안정성</strong>이라는 판단하에, 복잡한 소프트웨어 가상화 대신 물리 GPU를 특정 VM에 1:1로 매핑하는 <strong>GPU Passthrough</strong> 방식으로 변경. 결과적으로 성능 오버헤드 없이 AI 연산에 최적화된 환경을 구축</li></ul><hr><h2 id="5-결과-및-기대-효과"><a href="#5-결과-및-기대-효과" class="headerlink" title="5. 결과 및 기대 효과"></a>5. 결과 및 기대 효과</h2><ol><li><strong>공격 벡터 원천 차단:</strong> 포트 포워딩 미사용으로 외부 해킹 위협 최소화</li><li><strong>운영 편의성:</strong> 전용 앱 없이 브라우저 주소창만으로 외부에서 홈서버 기동 및 Proxmox 콘솔 접속 가능</li><li><strong>비용 절감:</strong> 필요할 때만 원격 부팅하여 가동함으로써 불필요한 전력 낭비 방지</li></ol><hr><h2 id="6-마치며"><a href="#6-마치며" class="headerlink" title="6. 마치며"></a>6. 마치며</h2><p>클라우드 엔지니어로 일하며 실무에서 다루던 리버스 프록시, 가상화, 네트워크 라우팅의 기본 원리들을 홈서버에 직접 녹여본 유익한 시간이었던 것 같다.</p><blockquote><p><strong>TO. 이 글을 보게 될 동생에게</strong></p><p>난 널 위해 최선을 다했는데 이 글을 보면 그놈의 알바 그만좀 하고 이제 죽기살기로 공부 좀 해라… 이정도면 너도 잘 알아듣었겠지? 할많하안할게…<br>그리고 VM서버 터졌다? 어차피 터져도 니쪽에서만 터지는거라 내꺼는 괜찮을거야. 서로 침범 못하게 권한 싹다 막아놨거든. 터지면 니껀 니가 복구해라 난 몰라 헤헤 ^^~~~<br>마지막으로 가이드 repo 초대했으니 잘 읽어보고 세팅하렴. 물론 질문은 안받을거임 솔직히 그정도로 README 자세하게 써줬으면 감사하다고 절해도 모자라지 않을까 싶네ㅎㅎ 모르면 AI한테 물어봐 나 진짜 바쁨 ㅅㄱ</p></blockquote>]]></content>
<categories>
<category> 🔐 Network/Security </category>
</categories>
<tags>
<tag> Network </tag>
<tag> Proxmox </tag>
<tag> Tailscale </tag>
<tag> ZeroTrust </tag>
<tag> WOL </tag>
<tag> Infrastructure </tag>
</tags>
</entry>
<entry>
<title>안쓰는 노트북을 홈서버로 개조해보자 (feat. Windows를 밀고 Ubuntu Server 세팅하기)</title>
<link href="/2026/02/28/Tips/UbuntuServer-Setting/"/>
<url>/2026/02/28/Tips/UbuntuServer-Setting/</url>
<content type="html"><![CDATA[<p>동생이 집에 홈서버 있으면 쓰고싶다 하길래 굳이 필요한가 싶어 빠꾸시켰는데, 생각을 해보니 하나 장만하면 동생이 컴퓨터 공부를 하는데 제격이겠다 싶어서 오랜만에 만들어봤다.</p><p>나는 집에서 안쓰는, 2017년인가에 구매한 삼성의 always9 노트북으로 홈서버를 만들었다.</p><h2 id="0-준비물"><a href="#0-준비물" class="headerlink" title="0. 준비물"></a>0. 준비물</h2><ul><li>Ubuntu Server 이미지를 구울 <strong>USB</strong></li><li>안쓰는 노트북 (여기서는 삼성 always9 사용)</li><li>사용 중인 노트북 (ubuntu server와의 통신을 위함)</li></ul><h2 id="1-Ubuntu-Server-이미지-다운로드"><a href="#1-Ubuntu-Server-이미지-다운로드" class="headerlink" title="1. Ubuntu Server 이미지 다운로드"></a>1. Ubuntu Server 이미지 다운로드</h2><ul><li>사이트: <a href="https://ubuntu.com/download/server">https://ubuntu.com/download/server</a></li></ul><p>일단 홈서버는, 컴퓨터가 계속 돌아간다는 가정이 있어야 하기 때문에 <strong>에너지를 가장 안 잡아먹는</strong>형태로 구현을 해야한다.</p><p>우리가 생각하는 우분투 데스크톱은 GUI가 있어 CPU, RAM같은 컴퓨터 자원을 많이 잡아먹기 때문에, 아주 최소한의 필수 기능만 들어있는 <strong>Ubuntu Server</strong>를 사용했다.</p><p>애초에 ubuntu server는 SSH를 통해 명령어를 입력하는 방식이 최적화되어 있기도 하고, attack surface도 적어서 다른 것들에 비해 매우 안전한 편이기도 하다.</p><p>다들 알겠지만 프로그램은 <strong>LTS</strong>버전이 가장 안정적이다!</p><h2 id="2-USB에-이미지-굽기"><a href="#2-USB에-이미지-굽기" class="headerlink" title="2. USB에 이미지 굽기"></a>2. USB에 이미지 굽기</h2><p>그냥 파일을 담으면 안된다. iso로 다운받아질텐데, 이거를 노트북에 꽂았을 때 <strong>부팅이 가능한 상태</strong>로 만들어야 한다.</p><p>그래서 windows를 사용 중이라면 <a href="https://rufus.ie/ko/">Rufus</a>를, 맥북을 사용 중이라면 <a href="https://etcher.balena.io/">BalenaEtcher</a>를 이용해서 usb를 구워준다.</p><h2 id="3-노트북에서-부팅-설정-잡기"><a href="#3-노트북에서-부팅-설정-잡기" class="headerlink" title="3. 노트북에서 부팅 설정 잡기"></a>3. 노트북에서 부팅 설정 잡기</h2><p>USB를 구웠다면, 이제 ubuntu server를 설치할 노트북의 부팅 설정을 잡아줘야 한다. 그래야 usb를 선택해서 기존의 환경을 싹 다 밀고 ubuntu server를 설치할 수 있기 때문이다.</p><p>노트북마다 전원키가 다를 수 있는 점을 참고하길 바라며, 여기서는 삼성 always9 세팅으로 설명을 적어본다.</p><ul><li>전원이 켜지자마자 <code>F2</code>를 연타하여 삼성 BIOS 설정 화면으로 들어간다.</li><li>필자의 기억이 정확히 안나서 해당 메뉴이 명확한지 확신이 안들지만 일단 이런 느낌의 항목을 찾을 수 있을 것이다. BIOS에서 아래의 세팅이 잘 되어 있는지 확인해보자<ul><li>Secure Boot (Control): Off 또는 Disabled</li><li>OS Mode Selection: UEFI가 들어있는 메뉴로 변경</li><li>Fast BIOS Mode: Off</li><li>Battery Life Extender+: On</li><li>USB S3 Wakeup: On</li></ul></li><li>위 설정을 완료했다면 Save를 한 후, 재부팅을 하자마자 다시 F2를 연타하자</li></ul><h2 id="4-Ubuntu-Server-설치하기"><a href="#4-Ubuntu-Server-설치하기" class="headerlink" title="4. Ubuntu Server 설치하기"></a>4. Ubuntu Server 설치하기</h2><p>여기까지 잘 됐다면, <code>Try or install Ubuntu Server</code>, <code>Ubuntu server with the hwe kernel....</code> 이라는 선택지가 나올 것이다.</p><p>여기에서 맨 위의 <code>Try or install Ubuntu Server</code>를 Enter로 선택하거나, 아니면 가만히 있음 알아서 Ubuntu Server 설치로 이동하게 될 것이다.</p><p>그렇게 1분 정도 기다리면 영어가 올라오면서, 기본 세팅이 되는 것을 확인할 수 있다.</p><ul><li>Language: English 선택 (한글 지원 안됨)</li><li>Network: 일단 나중에 설정할 예정이니 바로 Done으로 넘어가기</li><li>Storage Configuration: <code>Use an entire disk</code>를 선택하면 기존의 프로그램 및 파일들이 싹다 없어진다. 그리고 <code>Done</code>을 누르면 <code>Confirm destructive action</code>이 나오는데, <code>Continue</code>를 선택하면 이 순간 Windows OS와 영원한 작별인사를 하게 된다는 점을 알아두자</li><li>Profile Setup<ul><li>Your name: 본인 이름</li><li>Your server’s name: 서버 이름</li><li>Pick a username: 로그인 아이디</li><li>Password: 비밀번호</li></ul></li></ul><p>이 모든 세팅이 완료된다면, <code>Install complete</code>가 나오고 <code>Reboot Now</code>가 나오는데, Reboot now를 누른 후 바로 꽂혀있는 USB를 제거한다.</p><p>설치가 잘됐다면 <code>end ssh host keys...</code> <code>datasource datasourceone up xxx seconds</code>라고 하면서 무한커서가 나오는데, 세팅이 잘 됐다는 의미이다.</p><p>이 때 엔터를 한두번 하면 <code>login:</code>이라며 커서가 깜빡이는데, <code>Pick a username</code>에 입력했던 아이디를 입력, 엔터 후 password도 잘 입력을 하면 서버 로그인에 성공할 것이다.</p><h2 id="5-노트북-뚜껑-닫기-설정-Lid-Switch-설정"><a href="#5-노트북-뚜껑-닫기-설정-Lid-Switch-설정" class="headerlink" title="5. 노트북 뚜껑 닫기 설정 (Lid Switch 설정)"></a>5. 노트북 뚜껑 닫기 설정 (Lid Switch 설정)</h2><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/systemd/logind.conf</code></pre><p>아래 항목들을 다음같이 변경해준다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token assign-left variable">HandleLidSwitch</span><span class="token operator">=</span>ignore<span class="token assign-left variable">HandleLidSwitchExternalPower</span><span class="token operator">=</span>ignore<span class="token assign-left variable">HandleLidSwitchDocked</span><span class="token operator">=</span>ignore</code></pre><p>이후 변경사항을 적용해준다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> systemctl restart systemd-logind</code></pre><p>이 세팅을 해주면 노트북 뚜껑을 닫아도 프로그램이 계속 작동되는걸 볼 수 있을 것이다.</p><h2 id="6-와이파이-Wi-Fi-잡기"><a href="#6-와이파이-Wi-Fi-잡기" class="headerlink" title="6. 와이파이 Wi-Fi 잡기"></a>6. 와이파이 Wi-Fi 잡기</h2><p>외부와의 통신을 위해 와이파이를 잡아줘야 한다.</p><p>ubuntu server는 <code>netplan</code>이라는 도구를 이용해 네트워크를 관리해준다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">ip</span> <span class="token function">link</span></code></pre><p>위 명령어를 입력하면, <code>wlp...</code>로 시작하는 이름이 보일 것이다. (대부분 이름이 <strong>wlp1s0</strong>임)</p><p>이 이름을 잘 백업해놓자. 이후로 설정 파일을 만들어서 wifi 연결을 잡아줄 것이다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">vim</span> /etc/netplan/01-netcfg.yaml</code></pre><p>파일 내용은 다음과 같이 만들어준다.<br>참고로 <code>.</code>하나가 스페이스바 한 번이니, 형식에 맞춰서 잘 작성을 할 것을 권장한다. 의외로 여기에서 오류가 굉장히 많이 나더라;</p><pre class="language-bash" data-language="bash"><code class="language-bash">network:<span class="token punctuation">..</span>version: <span class="token number">2</span><span class="token punctuation">..</span>renderer: networkd<span class="token punctuation">..</span>wifis:<span class="token punctuation">..</span><span class="token punctuation">..</span>wlp1s0: <span class="token comment"># 아까 백업하라고 했던 네트워크 이름</span><span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span>dhcp4: <span class="token function">yes</span><span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span>access-points:<span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token string">"와이파이이름"</span><span class="token builtin class-name">:</span><span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span>password: <span class="token string">"비밀번호"</span></code></pre><p>이후, 파일의 권한을 소유자만 읽고 쓸 수 있게 변경해준다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">chmod</span> <span class="token number">600</span> /etc/netplan/01-netcfg.yaml</code></pre><p>이 변경사항을 적용한다. 그러면 인터넷이랑 연결이 될 것이다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">sudo</span> netplan apply</code></pre><p>만약에 인터넷 연결을 확인하고 싶다면, 다음의 명령어를 입력하면 된다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">ping</span> <span class="token parameter variable">-c</span> <span class="token number">3</span> google.com</code></pre><p>핑이 3번 잘 와리가리 했다면, 인터넷이 잘 통한다는 의미이다. 만약 “Temporary failure in name resolution….”같은 문장이 나오면 띄어쓰기나 와이파이 이름, 비밀번호 오타일 수 있으니 꼭 확인해보자</p><h2 id="7-IP-확인하기"><a href="#7-IP-확인하기" class="headerlink" title="7. IP 확인하기"></a>7. IP 확인하기</h2><p>홈서버는 만들어졌다. 근데 주소를 알아야 와리가리를 할 수 있기 때문에, IP를 꼭 알아내야 한다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">ip</span> addr show wlp1s0</code></pre><p>위 명령어를 입력하면, 어떤 내용들이 쫙 나오는데 여기에서 <code>inet</code>다음에 나오는 숫자를 확인하면 된다.<br><br>보통은 <code>192.168.0.xxx</code> 또는 <code>172.x.x.x</code>형식으로 나올 것이다.</p><h2 id="8-다른-컴퓨터에서-홈서버-연결해보기"><a href="#8-다른-컴퓨터에서-홈서버-연결해보기" class="headerlink" title="8. 다른 컴퓨터에서 홈서버 연결해보기"></a>8. 다른 컴퓨터에서 홈서버 연결해보기</h2><p>이제 모든 준비는 끝났다. 홈서버에 직접 접속을 확인해보자</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">ssh</span> <span class="token string">"Pick a username에 입력했던 값"</span>@<span class="token string">"서버IP주소"</span> <span class="token comment"># 예: ssh systemadmin@192.168.0.1</span></code></pre><p>맨 처음 접속하면 Are you sure you want to continue connecting? 이라는 문구가 나오는데, 바로 yes를 타이핑한다. 그러면 비밀번호를 입력하라는데, 비밀번호를 입력하면 로그인이 잘 될 것이다.</p><img src="/2026/02/28/Tips/UbuntuServer-Setting/1.png" class="" title="unbutu server 접속 결과물"><p><br><br><br></p><p>저기에서 자원을 효율적으로 관리하고 싶은 사람들은 k8s를 세팅해서 운영하면 된다.</p><p>세팅법 다 까먹은 줄 알았는데 아주 잘된다 좋아요~~</p>]]></content>
<categories>
<category> 💾 Etc </category>
</categories>
<tags>
<tag> Infrastructure </tag>
<tag> Ubuntu </tag>
<tag> Windows </tag>
</tags>
</entry>
<entry>
<title>Kubernetes 용어 완전 쉽게 이해하기</title>
<link href="/2026/02/28/DevOps/K8S-Easy/"/>
<url>/2026/02/28/DevOps/K8S-Easy/</url>
<content type="html"><![CDATA[<p>요즘 프로젝트 멘토링에서 K8S 수업을 하고 있는데, 다들 용어 습득에 매우 어려움을 겪으시더라… 그래서 <strong>아주 완전 쉽게</strong> 이해하실 수 있도록 준비해본게 나름 괜찮은 것 같아서 ^_^ 블로그 글로 한 번 남겨본다.</p><p>모든 개념이 들어있진 않으니 참고하세요</p><h2 id="1-Node-노드"><a href="#1-Node-노드" class="headerlink" title="1. Node 노드"></a>1. Node 노드</h2><p>여러분들 앞에 10년도 더 된, 사용하지 않는 고물 노트북이 있다고 가정해보자.</p><p>이거를 여러분들의 <strong>홈서버</strong>로 설정하여 프로그램을 돌리거나 gdrive같은 <strong>파일저장시스템</strong>을 만든다고 하자. 그리고 여러분들은 가족들 모두가 이용할 수 있도록 설정을 세분화 하고 싶다고 하자.</p><p>이 때 여러분들이 <strong>K8S</strong>로 시스템을 관리한다고 하면, node는 <strong>실제 물리적 공간 = 노트북</strong>이 된다.</p><p>그 중에서도</p><ul><li><strong>Master Node</strong>: 직접 일은 안하고 명령만 내리는 일종의 <strong>두뇌</strong> (예시에서는 노트북이 두뇌 역할을 겸하는 중)</li><li><strong>Worker Node</strong>: 쫄따구(Pod)가 실제로 거주하며 일하는 <strong>물리적 공간</strong>을 의미한다.</li></ul><h2 id="2-Namespace-네임스페이스"><a href="#2-Namespace-네임스페이스" class="headerlink" title="2. Namespace 네임스페이스"></a>2. Namespace 네임스페이스</h2><p>Namespace는 가상 구역을 나눌 때 사용한다. 즉 <strong>Node를 가상으로 N등분 한거</strong>라고 이해하면 된다.</p><p>노트북에서 A, B, C라는 Namespace(=구역)을 나누면 가족 1, 가족 2, 가족 3이 각각의 영역을 사용하면 된다는 얘기다.</p><p>분명 서버 테스트를 한다고 이것저것 돌려보면 오류가 날 가능성이 있다. 이 때 namespace를 기준으로 세팅을 하면 서로의 영역을 터치하지 않기 때문에 다른 쪽에서 에러가 났다한들 다른 프로그램은 잘 돌아가는걸 볼 수 있다.</p><h2 id="3-Pod-파드"><a href="#3-Pod-파드" class="headerlink" title="3. Pod 파드"></a>3. Pod 파드</h2><p>실제 이 체계에서 가장 <strong>쫄따구 / 노예</strong> 계급이라고 생각하면 된다. 얘가 실무 처리를 다 한다.</p><p>여기서 시스템 서버 돌리기 등이 일어난다.</p><p>만약 서비스에 사람들이 많이 들어온다? 그러면 일이 바쁘단 얘기가 되는 것이며, 그럼 노예를 더 뽑아서 일을 시켜야 한다는 의미가 되므로 Pod들이 우후죽순 여러 개가 생겨난다고 보면 된다.</p><p>반대로 서비스에 사람들이 줄어들었다, 그러면 필요없는 Pod들이 삭제된다.</p><p>즉, <strong>Pod들은 추가되거나 삭제되는 것에서 매우 자유롭다고 생각하면 된다.</strong> <del>마치 회사 인원감축 때 가장 잘리기 쉬운 말단사원처럼……</del></p><h2 id="4-Service-서비스"><a href="#4-Service-서비스" class="headerlink" title="4. Service 서비스"></a>4. Service 서비스</h2><p>현실세계에서 다른 집으로 이사를 갔다고 쳐보자. 집 주소는 바뀌겠지만 자신의 핸드폰 번호는 번호 변경 서비스 신청을 하지 않는 이상 <strong>그대로일 것이다</strong>.</p><p>앞서 <strong>Pod는</strong> 추가/삭제에 매우 자유롭다고 말했는데, 이 얘기는 바로 노예의 정보가 자주 바뀐다는 의미 = <strong>주소가 고정되어 있지 않다</strong>는 뜻이 된다.</p><p>우리 입장에서는 이 Pod에서 원하는 서버를 돌리는건데, 이걸 못찾는다? 그러면 이걸 매번 어디있는지 찾아서 돌아가라! 라고 명령을 내려야 하는게 여간 귀찮은 일이 아니다. 이 때 나오는 개념이 바로 <strong>Service</strong>이다.</p><p>Service를 사용하면 <strong>주소를 고정</strong>함으로써 Pod의 내용물이 바뀌더라도 알아서 변경 주소로 매핑이 되어 우리가 편하게 이용할 수 있다.</p><h2 id="5-Port-포트"><a href="#5-Port-포트" class="headerlink" title="5. Port 포트"></a>5. Port 포트</h2><p>보통의 회사같은 경우 마케팅 / IT / HR / 경영 부서가 각각 있을 것이다. 회사다보니 사람들이 이직 또는 퇴사를 하면서 인원변동이 있을 것이지만, 부서 자체는 사라지는 일이 거의 없다.</p><p>여기 k8s에서도 마찬가지다. 어쨌든간에 관리직이든 노예든 모두가 일을 할텐데, 현실세계에서 우리가 이직이나 퇴사를 하는 것처럼 얘들이 평생을 바쳐 일을 하지 않는다. 자원이 변경될 수 있다. 하지만 자원들이 <strong>속한 공간은 변경되지 않는다</strong>.</p><p>그래서 Port는 <strong>실제 일하는 공간의 주소</strong>라고 생각하면 된다.</p><ul><li>targetPort: Pod들이 실제로 머무는 주소 - 101동 101호같은 주소에서 <strong>101호</strong>를 의미</li><li>port: 서비스의 주소 - 여러 개의 서비스가 있으니 “A 서비스는 80번”, “B 서비스는 81번” 식으로 이름을 붙여줌</li><li>nodePort: 외부에서 노트북(Node) IP를 치고 들어올 때 쓰는 일종의 <strong>정문 번호</strong></li></ul><h2 id="6-Deployment-ReplicaSet"><a href="#6-Deployment-ReplicaSet" class="headerlink" title="6. Deployment / ReplicaSet"></a>6. Deployment / ReplicaSet</h2><p>위에서 pod들이 실질적인 일을 한다고 했는데, 여기서 deployment와 replicaSet은 그 pod들의 상사 역할이라고 생각하면 된다.</p><p>즉 <strong>Deployment > ReplicaSet > Pod</strong> 순이며,</p><ul><li>Deployment가 “새 버전 업데이트”, “롤백” 같은 <strong>전체적인 전략</strong>을 짜면</li><li>ReplicaSet이 Deployment의 명령을 받고 <strong>오직 Pod의 숫자 관리</strong>에 집중을 한다. 예를 들면 “지금 바빠 죽겠는데 pod가 하나네? 더 만들자” 이런식으로 된다.</li><li>그리고 실질적으로 Pod가 그 일을 한다고 생각하면 된다.</li></ul><p>그래서 실제로 명령어를 쳐보면,</p><ul><li>Deployment: my-web</li><li>ReplicaSet: my-web-123abc</li><li>Pod: my-web-random-123abc-def456</li></ul><p>이런식으로 종속되어 있는 형태로 이름이 지어지는 것을 알 수 있다.</p><h2 id="7-Ingress-Egress"><a href="#7-Ingress-Egress" class="headerlink" title="7. Ingress / Egress"></a>7. Ingress / Egress</h2><p>위에서 우리는 port라는 것을 설명했는데, port는 쉽게 “주소”를 얘기한다고 했다.</p><p>만약에 노트북을 홈서버로 만들었다고 하면, 보통은 공부 목적으로 프로젝트를 만들어 배포 연습도 할 것이다.</p><p>근데 그런 프로젝트가 한 두개여도 포트 번호를 외우는데 귀찮은데, 이게 20개 30개가 넘는다고 하자. 이걸 언제 다 외우겠는가??</p><p>이 때 필요한게 바로 <strong>Ingress</strong>이다. Ingress는 <strong>포트 번호를 외우기 싫을 때, 도메인에 연결해서 쉽게 우리의 포트와 매핑을 해주는 것</strong>이라고 생각하면 된다.</p><br><p>그럼 <strong>Egress</strong>는 무엇이냐, 일단 Ingress는 영단어만 보면 “들어온다”는 뜻이다. 즉 Egress는 그 반대로 “밖으로 나가는 것”에 대한 것이 된다.</p><p>그럼 데이터가 밖으로 나가는 경우가 무엇이 있을까? 보통은 사용자가 서버에서 데이터를 다운로드받을 때를 생각하겠지만, 여러 관점에서 생각해볼 때 <strong>해킹당했을 때 데이터가 밖으로 나갈 것</strong>도 고려해야 한다.</p><p>그래서 Egress는 <strong>데이터를 밖으로 꺼낼 때 얼마나 차단할지</strong>를 설정해서, 해킹 방지용에 많이 사용한다고 생각하면 된다.</p><h2 id="8-Volume"><a href="#8-Volume" class="headerlink" title="8. Volume"></a>8. Volume</h2><p>지금까지는 프로그램에 대한 얘기를 했다. pod를 통해 실질적으로 프로그램을 돌린다고 했는데, pod는 결국 노예이기 때문에 필요 없으면 바로 컷을 당하는 불쌍한 아이다.</p><p>그러면 이 얘기는 즉슨, pod가 사라지면 기존에 pod 안에 있던 데이터는 전부 하늘나라로 날아가게 된다는 얘기다. 우리의 데이터는 그럼 영원한 이별을 맞이하게 된다는 그런 슬픈 이야기가….</p><p>그럼 데이터를 안날아가게 하는 방법이 있냐? 당연히 있다. 이 때 나오는 개념이 바로 <strong>Volume</strong>이다.</p><p>Volume을 사용하면 <strong>하드디스크에 데이터를 안전하게 저장해서 영구 보관을 할 수 있다.</strong></p><p>노트북으로 서버를 만들었다면, 이 노트북의 하드디스크게 데이터를 저장하게 될 것이다.</p><p>우리는 이것을 <strong>PV(Persistent Volume)</strong> 이라고 부르며, 파일을 저장하고 관리하는 경험을 하고 싶을 때 <strong>PVC(Persistent Volume Claim)</strong> 이라는 저장소 요청소를 만들어서 k8s에 제출하면 파일을 업로드할 수 있다.</p>]]></content>
<categories>
<category> ♼ DevOps </category>
</categories>
<tags>
<tag> DevOps </tag>
<tag> Kubernates </tag>
</tags>
</entry>
<entry>
<title>[HealthCare] HL7 FHIR 완전 정리</title>
<link href="/2025/08/25/HealthCare/HL7/fhir/"/>
<url>/2025/08/25/HealthCare/HL7/fhir/</url>
<content type="html"><![CDATA[<h2 id="HL7-FHIR-완전-정리"><a href="#HL7-FHIR-완전-정리" class="headerlink" title="HL7 FHIR 완전 정리"></a>HL7 FHIR 완전 정리</h2><p>헬스데이터 스타트업이라면 <strong>FHIR</strong>은 반드시 이해해야 할 표준이다.<br>FHIR(Fast Healthcare Interoperability Resources)는 HL7이 v2, v3, CDA의 교훈을 바탕으로 만든 최신 국제 표준이다.</p><hr><h3 id="1-FHIR이란"><a href="#1-FHIR이란" class="headerlink" title="1. FHIR이란?"></a>1. FHIR이란?</h3><ul><li>풀네임: <strong>Fast Healthcare Interoperability Resources</strong></li><li>HL7이 2014년부터 개발한 차세대 표준 (현재 R5까지 출시)</li><li>웹/모바일 친화적인 설계를 목표로 함</li><li>JSON, XML, RDF 형식 지원 (주로 JSON 사용)</li><li>REST API, GraphQL, 메시징 등 다양한 통신 방식 지원</li></ul><hr><h3 id="2-왜-FHIR인가"><a href="#2-왜-FHIR인가" class="headerlink" title="2. 왜 FHIR인가?"></a>2. 왜 FHIR인가?</h3><ul><li><strong><a href="/2025/02/20/HealthCare/HL7/v2/" title="[HealthCare] HL7 v2 정리">v2</a> → 단순하지만 병원마다 제각각</strong></li><li><strong><a href="/2025/03/05/HealthCare/HL7/v3/" title="[HealthCare] HL7 v3의 실패 이유 정리">v3</a> → 구조적이지만 너무 복잡해서 실패</strong></li><li><strong><a href="/2025/08/02/HealthCare/HL7/cda/" title="[HealthCare] HL7 CDA 표준 정리">CDA</a> → 문서 교환에는 성공했지만 실시간 데이터 교환 한계</strong></li></ul><p>➡️ FHIR은 이 모든 문제를 개선했다.</p><ul><li>직관적인 JSON 구조</li><li>리소스(Resource) 단위 설계 (Patient, Observation, Encounter 등)</li><li>RESTful API로 쉽게 통신 가능</li><li>Implementation Guide(IG), 프로파일링/검증 도구 제공</li></ul><hr><h3 id="3-FHIR-리소스-Resource"><a href="#3-FHIR-리소스-Resource" class="headerlink" title="3. FHIR 리소스(Resource)"></a>3. FHIR 리소스(Resource)</h3><p>FHIR의 핵심은 <strong>리소스(Resource)</strong> 개념이다.<br>각각의 리소스가 의료 개념(환자, 진단, 검사 결과 등)을 표현한다.</p><p>대표적인 리소스:</p><ul><li><strong>Patient</strong> : 환자 정보</li><li><strong>Observation</strong> : 검사 결과, 활력징후</li><li><strong>Encounter</strong> : 진료/입원 방문</li><li><strong>Condition</strong> : 진단명</li><li><strong>MedicationRequest</strong> : 처방</li><li><strong>DocumentReference</strong> : 문서 (CDA와 연결됨)</li></ul><hr><h3 id="4-FHIR-JSON-예시"><a href="#4-FHIR-JSON-예시" class="headerlink" title="4. FHIR JSON 예시"></a>4. FHIR JSON 예시</h3><h4 id="4-1-Patient-리소스"><a href="#4-1-Patient-리소스" class="headerlink" title="4-1) Patient 리소스"></a>4-1) Patient 리소스</h4><pre class="language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"resourceType"</span><span class="token operator">:</span> <span class="token string">"Patient"</span><span class="token punctuation">,</span> <span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"12345"</span><span class="token punctuation">,</span> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"family"</span><span class="token operator">:</span> <span class="token string">"Kim"</span><span class="token punctuation">,</span> <span class="token property">"given"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"Minjun"</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token property">"gender"</span><span class="token operator">:</span> <span class="token string">"male"</span><span class="token punctuation">,</span> <span class="token property">"birthDate"</span><span class="token operator">:</span> <span class="token string">"1990-01-01"</span><span class="token punctuation">,</span> <span class="token property">"address"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"city"</span><span class="token operator">:</span> <span class="token string">"Seoul"</span><span class="token punctuation">,</span> <span class="token property">"country"</span><span class="token operator">:</span> <span class="token string">"KR"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token property">"telecom"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"phone"</span><span class="token punctuation">,</span> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"010-1234-5678"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">}</span></code></pre><h4 id="4-2-Observation-리소스-혈압"><a href="#4-2-Observation-리소스-혈압" class="headerlink" title="4-2) Observation 리소스 (혈압)"></a>4-2) Observation 리소스 (혈압)</h4><pre class="language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"resourceType"</span><span class="token operator">:</span> <span class="token string">"Observation"</span><span class="token punctuation">,</span> <span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"bp-001"</span><span class="token punctuation">,</span> <span class="token property">"status"</span><span class="token operator">:</span> <span class="token string">"final"</span><span class="token punctuation">,</span> <span class="token property">"category"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"coding"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://terminology.hl7.org/CodeSystem/observation-category"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"vital-signs"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"coding"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://loinc.org"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"85354-9"</span><span class="token punctuation">,</span> <span class="token property">"display"</span><span class="token operator">:</span> <span class="token string">"Blood pressure panel"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"subject"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"reference"</span><span class="token operator">:</span> <span class="token string">"Patient/12345"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"effectiveDateTime"</span><span class="token operator">:</span> <span class="token string">"2025-08-26T10:30:00+09:00"</span><span class="token punctuation">,</span> <span class="token property">"component"</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"coding"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://loinc.org"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"8480-6"</span><span class="token punctuation">,</span> <span class="token property">"display"</span><span class="token operator">:</span> <span class="token string">"Systolic blood pressure"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"valueQuantity"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"mmHg"</span><span class="token punctuation">,</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://unitsofmeasure.org"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"mm[Hg]"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"coding"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://loinc.org"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"8462-4"</span><span class="token punctuation">,</span> <span class="token property">"display"</span><span class="token operator">:</span> <span class="token string">"Diastolic blood pressure"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"valueQuantity"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">80</span><span class="token punctuation">,</span> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"mmHg"</span><span class="token punctuation">,</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://unitsofmeasure.org"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"mm[Hg]"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span><span class="token punctuation">}</span></code></pre><hr><h3 id="5-FHIR의-장점"><a href="#5-FHIR의-장점" class="headerlink" title="5. FHIR의 장점"></a>5. FHIR의 장점</h3><ul><li><strong>현대적 개발환경과 친화적</strong>: REST API, JSON, GraphQL 지원</li><li><strong>모듈성</strong>: 필요한 리소스만 조합해 사용</li><li><strong>프로파일링</strong>: 각 나라/기관이 자신만의 Implementation Guide(IG)를 정의 가능</li><li><strong>검증 도구 풍부</strong>: IG Publisher, HAPI-FHIR, Firely 등</li><li><strong>상호운용성 확보</strong>: 코드 체계(LOINC, SNOMED CT 등)와 연계</li></ul><hr><h3 id="6-실제-활용-사례"><a href="#6-실제-활용-사례" class="headerlink" title="6. 실제 활용 사례"></a>6. 실제 활용 사례</h3><ul><li><strong>미국 ONC/Meaningful Use</strong>: FHIR을 국가적 표준으로 지정</li><li><strong>애플 HealthKit</strong>: FHIR 기반 API로 병원과 데이터 연동</li><li><strong>Google Cloud Healthcare API, Microsoft Azure Health Data Services</strong>: 클라우드 벤더들이 FHIR API 기본 제공</li><li><strong>대한민국</strong>: 보건복지부 주도로 국가 EHR·PHR 사업에서 FHIR 채택 확산 중</li></ul><hr><h3 id="7-정리"><a href="#7-정리" class="headerlink" title="7. 정리"></a>7. 정리</h3><ul><li>FHIR은 HL7의 최신 표준으로, v2·v3·CDA의 장점을 모으고 단점을 극복했다.</li><li>JSON/REST 기반으로 직관적이고 개발자 친화적이다.</li><li>이미 글로벌 빅테크와 각국 정부가 채택하고 있으며, 앞으로의 의료 데이터 교환 표준은 사실상 FHIR로 수렴 중이다.</li></ul><p>👉 요약하면, <strong>FHIR은 현재와 미래 의료 데이터 교환의 핵심 표준</strong>이다.</p>]]></content>
<categories>
<category> 🏥 HealthCare </category>
</categories>
<tags>
<tag> EHR </tag>
<tag> HL7 </tag>
<tag> HealthCare </tag>
<tag> FHIR </tag>
</tags>
</entry>
<entry>
<title>[HealthCare] HL7 CDA 표준 정리</title>
<link href="/2025/08/02/HealthCare/HL7/cda/"/>
<url>/2025/08/02/HealthCare/HL7/cda/</url>
<content type="html"><![CDATA[<h2 id="HL7-CDA-표준-정리"><a href="#HL7-CDA-표준-정리" class="headerlink" title="HL7 CDA 표준 정리"></a>HL7 CDA 표준 정리</h2><p>헬스데이터 스타트업이라면 반드시 알아야 할 표준 중 하나가 바로 <strong>CDA (Clinical Document Architecture)</strong> 이다.<br>이 글에서는 CDA가 어떤 배경에서 등장했는지, 문서 구조는 어떻게 생겼는지, 그리고 실제 어디에 활용되는지를 정리한다.</p><hr><h3 id="1-CDA란"><a href="#1-CDA란" class="headerlink" title="1. CDA란?"></a>1. CDA란?</h3><ul><li><strong>Clinical Document Architecture (임상 문서 구조)</strong></li><li>HL7 v3에서 파생된 표준으로, <strong>임상 정보를 문서 형태</strong>로 전자 교환하기 위해 설계되었다.</li><li>“메시지 기반 교환”이었던 v2/v3와 달리, CDA는 <strong>문서(document) 단위</strong>에 초점을 맞췄다.</li><li>현재도 여러 국가의 전자건강기록(EHR) 사업, 보험청구, 진료 요약서 교환 등에 사용된다.</li></ul><hr><h3 id="2-CDA의-구조"><a href="#2-CDA의-구조" class="headerlink" title="2. CDA의 구조"></a>2. CDA의 구조</h3><p>CDA 문서는 크게 <strong>헤더(Header)</strong> 와 <strong>본문(Body)</strong> 으로 나뉜다.</p><ul><li><p><strong>헤더(Header)</strong></p><ul><li>환자 식별자, 작성자(의사/간호사), 작성일, 문서 종류(예: Discharge Summary) 등 메타데이터</li><li>문서를 해석할 때 기본 틀을 제공</li></ul></li><li><p><strong>본문(Body)</strong></p><ul><li>실제 임상 내용</li><li>진단, 검사 결과, 처방, 경과, 퇴원 요약 등</li><li>구조적(Structured) 본문 또는 자유 서술(Text) 본문 모두 가능</li></ul></li></ul><blockquote><p>그런데 CDA는 HL7 v3에서 파생된 표준이므로, v3와의 차이를 이해하는 것이 중요하다.</p></blockquote><h4 id="HL7-v3와-CDA의-차이"><a href="#HL7-v3와-CDA의-차이" class="headerlink" title="HL7 v3와 CDA의 차이"></a>HL7 v3와 CDA의 차이</h4><table><thead><tr><th>구분</th><th>HL7 v3</th><th>CDA (Clinical Document Architecture)</th></tr></thead><tbody><tr><td><strong>출발점</strong></td><td>HL7이 v2 한계를 극복하려고 만든 <strong>차세대 메시징 표준</strong></td><td>HL7 v3의 기술을 바탕으로 만들어진 <strong>문서 교환 표준</strong></td></tr><tr><td><strong>표현 방식</strong></td><td>XML 기반, RIM(Reference Information Model) 전체를 따름</td><td>XML 기반, <code><ClinicalDocument></code> 루트 + 헤더/본문 구조</td></tr><tr><td><strong>단위</strong></td><td><strong>메시지 단위</strong> (Observation, Order, Patient 등 이벤트 중심)</td><td><strong>문서 단위</strong> (진료요약서, 퇴원 요약, 검사 리포트 등)</td></tr><tr><td><strong>구조</strong></td><td>매우 복잡, <code>classCode</code>·<code>moodCode</code> 등 속성 다수, 깊은 중첩</td><td>비교적 단순, 문서 헤더 + 본문으로 나뉨</td></tr><tr><td><strong>상호운용성</strong></td><td>국가·기관별 Implementation Guide 필요 → 글로벌 호환 실패</td><td>특정 문서 템플릿 정의 → 국가 단위 사업(EHR, 보험)에서 실제 활용</td></tr><tr><td><strong>현실 평가</strong></td><td>이론적으론 완벽, 하지만 <strong>너무 복잡해 실패</strong></td><td>단순 문서 교환에는 성공, 여전히 현역</td></tr><tr><td><strong>다음 세대</strong></td><td>CDA와 FHIR로 분화됨</td><td>이후 <strong>FHIR 문서 리소스(DocumentReference 등)</strong> 로 진화</td></tr></tbody></table><blockquote><p>📌 정리:<br>HL7 v3는 <strong>메시지 교환 전체를 새롭게 정의하려 한 대실험</strong>이었고,<br>CDA는 그 안에서 <strong>임상 문서를 교환하는 실용적 프로파일</strong>로 태어난 성공 사례다.<br>따라서 <strong>v3는 실패했지만 CDA는 지금도 보험청구·국가 EHR에서 현역으로 쓰인다.</strong></p></blockquote><hr><h3 id="3-CDA-XML-예시-축약본"><a href="#3-CDA-XML-예시-축약본" class="headerlink" title="3. CDA XML 예시 (축약본)"></a>3. CDA XML 예시 (축약본)</h3><pre class="language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ClinicalDocument</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>urn:hl7-org:v3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token comment"><!-- 문서 식별자 --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>id</span> <span class="token attr-name">root</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2.16.840.1.113883.19.5<span class="token punctuation">"</span></span> <span class="token attr-name">extension</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>TT998<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>typeId</span> <span class="token attr-name">root</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2.16.840.1.113883.1.3<span class="token punctuation">"</span></span> <span class="token attr-name">extension</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>POCD_HD000040<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token comment"><!-- 환자 정보 --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>recordTarget</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>patientRole</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>id</span> <span class="token attr-name">extension</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>12345<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>patient</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>name</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>given</span><span class="token punctuation">></span></span>Minjun<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>given</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>family</span><span class="token punctuation">></span></span>Kim<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>family</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>name</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>birthTime</span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>19900101<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>administrativeGenderCode</span> <span class="token attr-name">code</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>patient</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>patientRole</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>recordTarget</span><span class="token punctuation">></span></span> <span class="token comment"><!-- 문서 본문 --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>component</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>structuredBody</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>component</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Discharge Summary<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>text</span><span class="token punctuation">></span></span>환자는 안정적인 상태로 퇴원하였다.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>text</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>component</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>structuredBody</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>component</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ClinicalDocument</span><span class="token punctuation">></span></span></code></pre><p>위 예시는 <strong>퇴원 요약서(Discharge Summary)</strong> 를 CDA 형식으로 기록한 단순 예시이다.<br>헤더에는 환자 이름과 성별, 생년월일이 들어가고, 본문에는 퇴원 소견이 서술돼 있다.</p><hr><h3 id="4-CDA의-특징과-장단점"><a href="#4-CDA의-특징과-장단점" class="headerlink" title="4. CDA의 특징과 장단점"></a>4. CDA의 특징과 장단점</h3><h4 id="장점"><a href="#장점" class="headerlink" title="장점"></a>장점</h4><ul><li><strong>문서 중심</strong>: 한 문서 안에 환자 맥락 + 임상 내용을 함께 담을 수 있다.</li><li><strong>표준화된 구조</strong>: 헤더/본문 구조가 명확하여 문서 간 교환이 용이하다.</li><li><strong>확장성</strong>: 자유 텍스트도 담을 수 있어 기존 문서 문화를 그대로 살릴 수 있다.</li><li><strong>호환성</strong>: 많은 국가 프로젝트(EHR, 보험)에서 채택되어 실제 현장 활용도가 높다.</li></ul><h4 id="단점"><a href="#단점" class="headerlink" title="단점"></a>단점</h4><ul><li><strong>여전히 XML 기반</strong>: 복잡하고 장황하다.</li><li><strong>구조적 제약</strong>: 본문에 자유 텍스트를 허용하면서도 구조적 요소를 강제해, 구현 난이도가 높다.</li><li><strong>실시간 교환 한계</strong>: CDA는 문서 단위라서, API처럼 세밀한 데이터 조회에는 적합하지 않다.</li></ul><hr><h3 id="5-실제-활용-사례"><a href="#5-실제-활용-사례" class="headerlink" title="5. 실제 활용 사례"></a>5. 실제 활용 사례</h3><ul><li><p><strong>국가 단위 EHR 사업</strong>:</p><ul><li>미국 Meaningful Use 프로그램에서 CDA 기반의 “Continuity of Care Document (CCD)” 활용</li><li>한국, 일본 등도 국가 표준 문서 교환에 CDA를 활용</li></ul></li><li><p><strong>보험 청구</strong>:</p><ul><li>진료 요약서, 처방전, 검사 리포트를 CDA 문서로 교환</li></ul></li><li><p><strong>병원 간 진료 정보 공유</strong>:</p><ul><li>환자가 다른 병원으로 전원될 때, CDA 퇴원 요약서로 정보를 전송</li></ul></li></ul><hr><h3 id="6-정리"><a href="#6-정리" class="headerlink" title="6. 정리"></a>6. 정리</h3><ul><li><strong>CDA</strong>는 HL7 v3에서 파생된 문서 교환 표준이다.</li><li>문서 단위로 환자 정보와 임상 데이터를 전달할 수 있어, 보험/진료요약/국가사업에서 여전히 많이 쓰인다.</li><li>그러나 XML 기반 특유의 장황함과 실시간 교환 한계 때문에, 최신 세대인 <strong>FHIR</strong>이 부상하게 되었다.</li></ul><p>👉 요약하면, <strong>CDA는 v3의 유산이지만 여전히 현역에서 쓰이는 표준</strong>이며, FHIR로 이어지는 진화의 징검다리다.</p>]]></content>
<categories>
<category> 🏥 HealthCare </category>
</categories>
<tags>
<tag> EHR </tag>
<tag> HL7 </tag>
<tag> HealthCare </tag>
<tag> CDA </tag>
</tags>
</entry>
<entry>
<title>직접 개발한 사이트, 애드센스 등록 성공!</title>
<link href="/2025/07/10/Etc/Build-Site-Adsense/"/>
<url>/2025/07/10/Etc/Build-Site-Adsense/</url>
<content type="html"><![CDATA[<p>예전부터 직접 만든 사이트에 애드센스를 달아보고 싶었는데, 이번에 애드센스를 무사히 통과했다!</p><p>이번 포스팅에서는 어떤 방식으로 만들었고, 심사를 위해 어떤 부분을 신경 썼는지, 그리고 실제 심사 전략까지 기록해본다.<br>검색해보니까 대부분 github page template이나 tistory써서 이런 글은 많이 없더라.. 글 많이 써주세요 동료님들 🥹</p><hr><h2 id="도메인-전략-루트도메인-vs-서브도메인"><a href="#도메인-전략-루트도메인-vs-서브도메인" class="headerlink" title="도메인 전략: 루트도메인 vs 서브도메인"></a>도메인 전략: 루트도메인 vs 서브도메인</h2><p>2024년까지는 애드센스가 <strong>서브도메인도 개별 등록 대상</strong>이었다.<br>하지만 규정이 바뀌면서 2025년부터는 <strong>루트 도메인만 등록하면 서브도메인 전체에 적용 가능</strong>하다는 식으로 변경되었다.</p><p>나는 <a href="https://anb-network.com/"><code>anb-network.com</code></a>이라는 도메인을 <strong>개인사업자 홍보용 사이트</strong>로 사용할 예정이었기 때문에, 애드센스 심사 페이지로 쓰기에는 다소 <strong>상업적 냄새가 강할 것</strong>이라 판단했다.</p><p>그래서 혹시 모를 사태를 대비해 서브도메인으로 따로 제작한 <a href="https://mentoring-career.anb-network.com/"><code>mentoring-career.anb-network.com</code></a> 사이트를 <strong>잠시 루트 도메인에 업로드</strong>해 심사를 진행했고, 애드센스가 통과되자 다시 서브도메인으로 돌려놓았다.<br>이 방식은 <strong>심사 과정에서 상업성 판단을 회피하는 전략</strong>으로 꽤 유효했던 것 같다.</p><hr><h2 id="프론트엔드-구조-SSR-서버사이드-렌더링-선택-이유"><a href="#프론트엔드-구조-SSR-서버사이드-렌더링-선택-이유" class="headerlink" title="프론트엔드 구조: SSR (서버사이드 렌더링) 선택 이유"></a>프론트엔드 구조: SSR (서버사이드 렌더링) 선택 이유</h2><p>Next.js를 사용했고, 기본 설정은 <code>app router</code> 기반 SSR이었다.<br>정적 페이지보다 <strong>서버 사이드 렌더링이 콘텐츠 신뢰도와 구조화된 데이터 면에서 유리하다는 글</strong>을 많이 참고했고, 실제로 <code>metadata</code> 구조가 깔끔하게 반영되는 것도 SSR의 장점 중 하나였다.</p><p>실제로 <code><head></code> 내부에 다음과 같은 메타데이터를 포함시켰다:</p><pre class="language-javascript" data-language="javascript"><code class="language-javascript"><span class="token keyword">export</span> <span class="token keyword">const</span> metadata <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">"알아보기 | 원격영상 진로멘토링"</span><span class="token punctuation">,</span> <span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">"클라우드 엔지니어가 하는 일, 필요한 기술, 진로 정보, 하루 일과 등을 친절하게 소개한 사전 수업용 진로 가이드입니다."</span><span class="token punctuation">,</span> <span class="token literal-property property">keywords</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"클라우드 엔지니어"</span><span class="token punctuation">,</span> <span class="token string">"진로 정보"</span><span class="token punctuation">,</span> <span class="token string">"IT 진로"</span><span class="token punctuation">,</span> <span class="token string">"클라우드 기술"</span><span class="token punctuation">,</span> <span class="token string">"DevOps"</span><span class="token punctuation">,</span> <span class="token string">"SRE"</span><span class="token punctuation">,</span> <span class="token string">"플랫폼 엔지니어"</span><span class="token punctuation">,</span> <span class="token string">"하루 일과"</span><span class="token punctuation">,</span> <span class="token string">"진로체험"</span><span class="token punctuation">,</span> <span class="token string">"사전 학습"</span><span class="token punctuation">,</span> <span class="token string">"AI 시대 클라우드"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token literal-property property">alternates</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">canonical</span><span class="token operator">:</span> <span class="token string">"https://mentoring-career.anb-network.com/info"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token literal-property property">robots</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">index</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token literal-property property">follow</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token literal-property property">openGraph</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"article"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p>심사에 큰 영향을 준 것은 단순히 키워드가 아니라, <strong>페이지마다 고유의 타이틀과 설명, <code>index/follow</code> 로봇 태그를 명시</strong>했다는 점이다.</p><hr><h2 id="사이트-구조-및-페이지-수-고려"><a href="#사이트-구조-및-페이지-수-고려" class="headerlink" title="사이트 구조 및 페이지 수 고려"></a>사이트 구조 및 페이지 수 고려</h2><p>애드센스는 심사 과정에서 <strong>“신뢰할 수 있는 사이트”</strong> 인지를 상당히 엄격하게 본다.<br>그 기준 중 하나가 <strong>페이지 수</strong>와 <strong>기초 페이지의 존재 여부</strong>다.</p><p>나는 다음 <strong>3개의 필수 페이지</strong>를 구성했다:</p><ul><li><code>/about</code> – 사이트 운영 목적과 배경 설명</li><li><code>/privacy</code> – 개인정보 처리방침</li><li><code>/terms</code> – 이용 약관</li><li><code>/contact</code> – 이메일 기반 문의처는 페이지 없이 mailto로 간단하게 구성했다.</li></ul><p>그 외에 <strong>메인 콘텐츠 페이지는 최소 3개 이상</strong>으로 구성했다:</p><ul><li><code>/info</code> – 직업 소개 / 진로 가이드</li><li><code>/deploy</code> – 클라우드 배포 실습</li><li><code>/game</code> – 클라우드 엔지니어 시뮬레이션 게임. 직접 js로 간단한 카드게임을 구현했다.</li></ul><p>실제로 <strong>콘텐츠 페이지가 1~2개뿐이면 거절 사례</strong>가 많다는 글을 많이 봤기에,<br>최소 3개 이상의 콘텐츠를 확보하려 노력했다.</p><hr><h2 id="기타-체크한-항목들"><a href="#기타-체크한-항목들" class="headerlink" title="기타 체크한 항목들"></a>기타 체크한 항목들</h2><ul><li><code>sitemap.xml</code> 직접 생성하여 Google Search Console에 제출</li><li><code>robots.txt</code> 작성</li><li>RSS는 제출하지 않음: 직업 체험 사이트 특성상 자주 업데이트되지 않기 때문</li><li>모든 페이지의 헤더, 푸터 일관성 유지</li><li>모바일 대응 + 기본적인 반응형 처리 완료: 일일이 만들기 귀찮아서 tailwind로 처리</li><li>광고단위 생성 및 활용: 광고 노출 시 원하는 형태의 광고를 송출할 수 있도록 세팅</li></ul><p>이 모든 항목들을 고려해서 그런지, 심사를 요청한지 약 18시간 만에 바로 <strong>합격</strong>을 받을 수 있었다!</p><hr><h2 id="통과-이후-전략"><a href="#통과-이후-전략" class="headerlink" title="통과 이후 전략"></a>통과 이후 전략</h2><p>애드센스가 통과된 이후, 루트 도메인(<a href="https://anb-network.com/"><code>anb-network.com</code></a>)은 원래 목적대로 <strong>개인사업자 홍보용</strong>으로 구성했다.<br>심사를 위해 잠시 루트 도메인에 걸었던 직업 체험 사이트는<br><strong>서브도메인(<a href="https://mentoring-career.anb-network.com/"><code>mentoring-career.anb-network.com</code></a>)으로 다시 돌려서 운영 중이다.</strong></p><p>애드센스 코드 자체는 <strong>서브도메인에 삽입해도 정상 노출</strong>된다.<br>동일하게 ads.txt를 생성하고, 루트도메인의 ads.txt내용을 그대로 복붙하면 된다.</p><hr><h2 id="마무리"><a href="#마무리" class="headerlink" title="마무리"></a>마무리</h2><p>나도 이전에는 맨날 오픈소스 템플릿만 가지고, 혹은 티스토리로 애드센스를 도전했는데,<br>이번에 애드센스 승인 사이트를 직접 만들면서 특히 SEO관점에서 많은 성장을 한 것 같다.</p><p>혹여나 직접 만든 사이트에 애드센스를 달 예정이신 분들이 계시다면 도움이 되길 바란다 🍀</p>]]></content>
<categories>
<category> 💾 Etc </category>
</categories>
<tags>
<tag> Adsense </tag>
<tag> NextJS </tag>
</tags>
</entry>
<entry>
<title>[GCP] GCP Professional Cloud Architect(PCA) 자격증 시험 준비 및 합격 후기</title>
<link href="/2025/06/10/Certificate/GCP/GoogleCloudPCA/"/>
<url>/2025/06/10/Certificate/GCP/GoogleCloudPCA/</url>
<content type="html"><![CDATA[<h2 id="GCP-사전-지식"><a href="#GCP-사전-지식" class="headerlink" title="GCP 사전 지식"></a>GCP 사전 지식</h2><ul><li>없음. 대충 들은건 있어가지고 어떤 서비스가 있는지는 아는데, 회사에서 AWS만 주구장창 쓰고 있으며, 회사가 너무 좋은 바람에 이직을 못하고 있어 다른 클라우드는 솔직히 써본 적이 없음.</li><li>즉, 전반적인 클라우드 지식은 알고있다만 <strong>Google Cloud 아키텍쳐</strong>에는 전혀 익숙하지 않았던 상태였으며 기초부터 공부를 시작함</li></ul><h2 id="시험-준비-기간-1달-인데-빡집중한거는-2주-정도"><a href="#시험-준비-기간-1달-인데-빡집중한거는-2주-정도" class="headerlink" title="시험 준비 기간: 1달 (인데 빡집중한거는 2주 정도)"></a>시험 준비 기간: 1달 (인데 빡집중한거는 2주 정도)</h2><h3 id="1-전반적인-개념-정리-하루"><a href="#1-전반적인-개념-정리-하루" class="headerlink" title="(1) 전반적인 개념 정리: 하루"></a>(1) 전반적인 개념 정리: 하루</h3><ul><li>Google Cloud Architecture Framework를 중심으로 문서 정독</li><li>IAM, VPC 피어링, Interconnect, Load Balancer 타입 구분 등 기본 정리</li><li>총 2-3페이지 정도로 압축 요약을 진행. 꽤 요약을 잘했는지 여기에서 시험문제가 다 나와서 편하게 응시한 것 같다.</li></ul><h3 id="2-시험-문제-공부-덤프-무료"><a href="#2-시험-문제-공부-덤프-무료" class="headerlink" title="(2) 시험 문제 공부: 덤프 (무료)"></a>(2) 시험 문제 공부: 덤프 (무료)</h3><ul><li>덤프: 일부 문제의 답에 대해서 갑론을박이 있으며, 이는 chatGPT를 활용해서 맞춰보는 것을 추천함<ul><li><a href="https://www.examtopics.com/exams/google/professional-cloud-architect/view/">Examtopics</a><ul><li>AWS 시험볼 때도 사용했던 사이트</li><li>구글 캐시를 이용하면 이후 문제도 무료로 풀 수 있었으나, <strong>2025년 5월 기준으로 막힌 것으로 확인됨</strong>. 무료 문제를 풀거라면 차라리 아래 사이트를 이용하는 것을 추천함</li></ul></li><li><a href="https://www.examprepper.co/exam/4/1">ExamPrepper</a><ul><li>Examtopics와 비슷한 무료 덤프 사이트. 여기서 실제 시험 문제를 많이 볼 수 있었음</li></ul></li><li>Case Study에서는 가장 최적의 효율을 낼 수 있는 답안을 선택<ul><li>PCA에서는 Mountkirk Games, TerramEarth만 나온다. 나머지는 공부 안해도 된다.</li></ul></li></ul></li><li>[참고] 유료 문제는 Udemy에서 많이 구매해 푸는 것 같다. 필자는 ExamPrepper만 풀어봐서 모르겠다. 개인적으로 ExamPrepper를 추천하는 것이, 필자는 이 사이트를 시험보기 3시간 전에 알게되어 급하게 풀어본거였는데, 문제가 여기서 15개나 나왔다.</li></ul><h2 id="시험-정보-팁"><a href="#시험-정보-팁" class="headerlink" title="시험 정보 & 팁"></a>시험 정보 & 팁</h2><ul><li>시험 언어: 영어 (한국어 미지원)</li><li>시험 방식: 온라인 / 오프라인<ul><li>아래 적어놓겠지만, 보안이 매우 빡세기 때문에 세팅이 귀찮다면 오프라인으로 보는 것을 추천한다.</li></ul></li><li>문제 수: 50문제<ul><li>필자의 경우, 1번부터 10번까지 Case Study 문제가</li><li>이후로는 기본적인 GCP 및 클라우드 관련 문제가 출제됨</li></ul></li><li>시험 시간: 2시간</li><li>합격 점수 기준: 80% 이상</li></ul><p>구글 자격증은 클라우드 업계에서도 어렵다고 소문이 자자한 자격증이다. 그리고 인터넷에 검색해보면 생각보다 한국어 후기가 그렇게 많지 않다.</p><p>공부를 시작한 당시 왜 그런가 싶었는데, 알고보니 일단 언어의 벽이 가장 큰 문제였다. 시대가 어느땐데 아직도 한국어 지원이 안됨 ㅎㅎㅎㅎ 이게 바로 대기업?!</p><h2 id="시험-신청-온라인-및-준비물"><a href="#시험-신청-온라인-및-준비물" class="headerlink" title="시험 신청 (온라인) 및 준비물"></a>시험 신청 (온라인) 및 준비물</h2><ul><li>시험 신청: <a href="https://www.webassessor.com/googlecloud">https://www.webassessor.com/googlecloud</a> -> Access Webassessor -> 원하는 형태의 시험을 선택할 것<ul><li>webassessor에 접속 후, My Assessments로 이동 후<ul><li>Profile에 들어가서 Legal First Name, Legal Last Name 스펠링을 확인할 것</li><li>Biometric Profile도 확인할 것<ul><li>될 수 있으면 낮에 사진을 찍는 것을 추천한다. 필자는 늦게 알아서 밤에 사진을 찍었는데, 빛에 되게 민감하다. 스탠드 불 한칸 가지고 너무 밝고 어둡다고 하더라;</li></ul></li><li>(온라인 시험 한정) Install New Secure Browser 클릭하여 Secure Browser 프로그램을 설치할 것</li></ul></li></ul></li><li>마이크, 웹캠 관련 시스템 설정: <a href="https://www.kryteriononline.com/systemcheck">https://www.kryteriononline.com/systemcheck</a></li><li>기타 준비물: ID카드 (여권)<ul><li>온라인 시험을 보면, 보안 검색이 끝난 후 화장실 및 물을 가져올 수 있는 시간을 준다. 필자는 새벽에 시험을 보는 상황이었던지라 바로 시험으로 간다고 해서 시험을 봤다.</li></ul></li></ul><h2 id="시험-팁"><a href="#시험-팁" class="headerlink" title="시험 팁"></a>시험 팁</h2><ul><li>PC에서 Case Study는 무조건 Mountkirk Games, TerramEarth에서만 나오는 것 같다. 인터넷에 여러 번 검색을 해봤는데, 모두가 하나같이 이 둘 말고는 전혀 나오지 않았다고 한다.<ul><li>개인적으로 Case Study가 가장 어려웠던 것 같다. 다른 것들은 딱봐도 정답이고 아니고를 판별할 수 있었는데, 이거는 선택지가 전부 말이 되기 때문이었다.</li><li>필자는 문제에서 특별한 요구사항이 없는 한, “비용효율적”인 방향으로 정답을 골랐다. 이것도 문제를 많이 풀면 적중률은 올라갈 수 있겠으나… 모르겠다. 너무 어려웡</li></ul></li><li>나같이 평범한 한국인이라면, 대부분 시험을 영어로 볼 것이다. 우리가 외국인이라고 추가시간을 주지 않는다. 이 쯤되면 AWS가 그리워진다…</li><li>AWS와 마찬가지로 GCP에서도 온라인 스터디 여는데, 참여 시 50% 시험응시 할인 쿠폰을 받을 수 있다. 원래 회사에서 시험비도 지원해줘서 굳이 들어갈 필요는 없었으나 공식스터디라고 하니까 혹여나 해서 2025년 상반기에 참여했는데, 스터디가 생각보다 도움이 안되어서 중도하차를 했다. 시험비를 아낄 목적이라면 적당히 들어가서 최소조건만 만족하고 바우처를 받으면 좋을 것 같다.</li></ul><h2 id="시험-후기"><a href="#시험-후기" class="headerlink" title="시험 후기"></a>시험 후기</h2><ul><li>총 50문제 나왔고 검토는 1번해서 1시간만에 끝냈다. AWS에서도 언급했듯이, 내가 잘한다는 의미는 아니고 찍거나 헷갈리는 문제는 전부 틀리는 사람인지라 굳이 시간낭비를 하기 싫었다 ㅎㅎ;; 무엇보다 이때 새벽 3시 30분에 시험봐서 그냥 자고싶었음ㅠㅠ</li><li>보안 개빡세다. 여기도 공항 몸수색 뺨치는 수준으로 본다. 책상만 하더라도 왼쪽, 오른쪽, 앞, 뒤, 모서리 사방을 전부 찍으라고 하더라. 심지어 노트북을 주변 한바퀴 천천히 돌려서 카메라에 잘 담기게 해달라고 요청도 한다. 한번 적당히 돌려줬는데 너무 빠르다고 다시해달라 하더라..</li><li>합불여부는 시험 끝내면 바로 볼 수 있다. 단 점수는 안나온다.</li><li>시험 난이도는.. Case Study가 긴가민가 했으나 그 외로는덤프에 나왔던거 그대로 나온게 많아서 괜찮았다.</li><li>시험에 통과하면 다음 GCP 자격증 시험 50%를 할인 바우처를 받을 수 있다.</li><li>일부 블로그 글에는 시험 합격 시 GCP굿즈를 받을 수 있다고 하는데, 올해 3월부터 해당 혜택이 사라졌다고 한다. 요즘 사정이 안좋다고 들었는데 생각보다 많이 안좋은가보다..ㅠㅠ</li></ul><img src="/2025/06/10/Certificate/GCP/GoogleCloudPCA/gcp_pca.avif" class="" title="GCP Professional Cloud Architect 자격증">]]></content>
<categories>
<category> 📝 Certificate </category>
</categories>
<tags>
<tag> Cloud </tag>
<tag> GCP </tag>
</tags>
</entry>
<entry>
<title>[HealthCare] HL7 v3의 실패 이유 정리</title>
<link href="/2025/03/05/HealthCare/HL7/v3/"/>
<url>/2025/03/05/HealthCare/HL7/v3/</url>
<content type="html"><![CDATA[<h2 id="HL7-v3의-실패-이유-정리"><a href="#HL7-v3의-실패-이유-정리" class="headerlink" title="HL7 v3의 실패 이유 정리"></a>HL7 v3의 실패 이유 정리</h2><p>헬스데이터 스타트업이라면 HL7 v2 말고도 <strong>v3의 역사와 실패 이유</strong>를 반드시 알아야 한다.<br>이 글에서는 HL7 v3가 어떤 배경에서 등장했고, 왜 실패했는지, 그리고 이후 표준에 어떤 영향을 미쳤는지 정리한다.</p><hr><h3 id="1-HL7-v3란"><a href="#1-HL7-v3란" class="headerlink" title="1. HL7 v3란?"></a>1. HL7 v3란?</h3><ul><li>2000년대 초반 HL7이 새롭게 만든 표준이다.</li><li><strong>XML 기반</strong>으로 더 엄격하고 체계적인 데이터 구조를 제공한다.</li><li>메시지를 <strong>Reference Information Model (RIM)</strong> 이라는 추상 데이터 모델에 맞춰 표현한다.</li><li>목표: v2의 자유로운(때로는 제각각인) 메시지 구조 문제를 해결하고, 전 세계적으로 일관된 모델을 제시하는 것.</li></ul><hr><h3 id="2-HL7-v3의-특징과-장단점"><a href="#2-HL7-v3의-특징과-장단점" class="headerlink" title="2. HL7 v3의 특징과 장단점"></a>2. HL7 v3의 특징과 장단점</h3><h4 id="장점"><a href="#장점" class="headerlink" title="장점"></a>장점</h4><ul><li><strong>구조적 일관성</strong>: 모든 메시지가 RIM이라는 공통 모델을 기반으로 작성된다.</li><li><strong>표준 코드 체계 사용</strong>: LOINC, SNOMED CT 같은 코드 시스템을 활용하여 의미를 명확히 표현할 수 있다.</li><li><strong>기계 판독성</strong>: XML 기반이므로 파싱과 검증이 용이하다.</li></ul><h4 id="단점"><a href="#단점" class="headerlink" title="단점"></a>단점</h4><ul><li><strong>과도한 복잡성</strong>:<ul><li>메시지 안에 <code>classCode</code>, <code>moodCode</code>, <code>codeSystem</code>, <code>interpretationCode</code> 등 필수 속성이 너무 많다.</li><li>개발자는 값 하나를 표현하기 위해 수많은 태그와 속성을 채워야 한다.</li></ul></li><li><strong>깊은 중첩 구조</strong>:<ul><li>Observation 안에 component/observation이 계속 중첩된다.</li><li>사람이 읽고 쓰기에 가독성이 떨어지고, 시스템 구현도 어렵다.</li></ul></li><li><strong>nullFlavor 문제</strong>:<ul><li>값이 없을 때도 단순히 비워둘 수 없고 <code>nullFlavor="UNK"</code> 같은 코드로 반드시 표현해야 한다.</li><li>각 기관이 이를 다르게 해석하면서 상호운용성이 깨졌다.</li></ul></li><li><strong>국가별 Implementation Guide(IG) 의존</strong>:<ul><li>HL7 v3 자체만으로는 너무 추상적이어서, 실제 사용을 위해서는 각 나라/기관이 IG를 별도로 만들어야 했다.</li><li>결과적으로 “국제 표준”인데도 불구하고, 국경을 넘으면 호환성이 보장되지 않았다.</li></ul></li></ul><hr><h3 id="3-결과-왜-실패했는가"><a href="#3-결과-왜-실패했는가" class="headerlink" title="3. 결과: 왜 실패했는가?"></a>3. 결과: 왜 실패했는가?</h3><ul><li><p><strong>현실적 구현 난이도 ↑</strong><br>→ 복잡한 XML 구조와 필드 정의 때문에 실제 시스템에 적용하기 어려웠다.</p></li><li><p><strong>기관별 해석 차이 ↑</strong><br>→ 같은 모델을 두고도 IG마다 다르게 구현, 결국 “표준이지만 표준이 아닌” 상황이 되었다.</p></li><li><p><strong>개발자/벤더 채택 저조</strong><br>→ v2 메시지는 단순해서 인터페이스 개발자가 빠르게 붙일 수 있었지만, v3는 학습곡선이 너무 높았다.</p></li><li><p><strong>상호운용성 확보 실패</strong><br>→ 원래 목표는 글로벌 상호운용성 확대였으나, 오히려 v2보다도 통합이 어려워졌다.</p></li></ul><p>➡️ 결국 HL7 v3는 <strong>국제적으로 널리 쓰이지 못하고 사실상 실패한 표준</strong>이 되었다.</p><hr><h3 id="4-이후로-이어진-흐름"><a href="#4-이후로-이어진-흐름" class="headerlink" title="4. 이후로 이어진 흐름"></a>4. 이후로 이어진 흐름</h3><ul><li><p><strong>CDA (Clinical Document Architecture)</strong></p><ul><li>v3의 일부 기술을 가져와 문서 중심 교환 표준으로 자리 잡았다.</li><li>환자 퇴원요약서, 진료 기록, 검사 리포트 등 <strong>문서 단위</strong> 교환에 성공적으로 적용.</li><li>지금도 국가 단위 EHR 프로젝트나 보험청구에서 쓰인다.</li></ul></li><li><p><strong>FHIR (Fast Healthcare Interoperability Resources)</strong></p><ul><li>v3의 복잡성을 교훈 삼아 2010년대에 등장.</li><li>JSON/XML 기반 + REST API 친화적.</li><li>구조가 단순하고 직관적이라 빠르게 전 세계에서 채택 중.</li></ul></li></ul><hr><h3 id="5-정리"><a href="#5-정리" class="headerlink" title="5. 정리"></a>5. 정리</h3><ul><li><p>HL7 v3는 <strong>이론적으로는 완벽</strong>했으나,</p><ul><li>너무 복잡한 XML 구조,</li><li>nullFlavor 등 과도한 제약,</li><li>국가별 IG에 의존한 해석 차이<br>때문에 <strong>실제 현장에서는 실패</strong>했다.</li></ul></li><li><p>그러나 v3의 시도는 헛되지 않았다.</p><ul><li>CDA라는 문서 교환 표준이 성공적으로 자리잡았고,</li><li>이후 FHIR이 “현실적이고 개발자 친화적인 표준”으로 발전할 수 있는 기반이 되었다.</li></ul></li></ul><p>👉 요약하면, <strong>v3는 실패했지만 CDA와 FHIR로 이어진 진화 과정에서 중요한 디딤돌이었다.</strong></p>]]></content>
<categories>
<category> 🏥 HealthCare </category>
</categories>
<tags>
<tag> EHR </tag>
<tag> HL7 </tag>
<tag> HealthCare </tag>
<tag> CDA </tag>
</tags>
</entry>
<entry>
<title>[Java] JDK 버전, javac와 java 차이, 그리고 IDE 빌드 시스템 정리</title>
<link href="/2025/02/28/Language/Java-faq/"/>
<url>/2025/02/28/Language/Java-faq/</url>
<content type="html"><![CDATA[<p>스프링(Spring)이나 자바(Java)를 공부하다 보면 가장 먼저 부딪히는 부분이 있다.<br>바로 <strong>JDK 버전 차이</strong>, <strong>javac/java 명령어</strong>, 그리고 <strong>Gradle, Maven 같은 빌드 시스템</strong>이다.<br>처음 접할 때는 개념이 섞여서 헷갈리기 쉬운데, 한 번에 큰 그림을 잡아두면 이후 학습이 훨씬 수월해진다.</p><p>이번 글에서는 실제로 많이 묻는 질문들을 묶어서 정리해본다.</p><p><br><br></p><h2 id="Java-8과-Java-21의-차이"><a href="#Java-8과-Java-21의-차이" class="headerlink" title="Java 8과 Java 21의 차이"></a>Java 8과 Java 21의 차이</h2><hr><p>많은 강의가 여전히 <strong>Java 8</strong>을 기준으로 설명한다.<br>하지만 요즘 개발 환경에서는 <strong>Java 17</strong>이나 <strong>Java 21</strong> 같은 최신 LTS(Long Term Support) 버전을 더 자주 사용한다.</p><ul><li><p><strong>Java 8 (2014 출시)</strong></p><ul><li>람다(Lambda), 스트림(Stream), Optional, 새로운 날짜/시간 API가 도입되었다.</li><li>이 시점을 기점으로 현대적인 자바 문법이 자리 잡았다.</li></ul></li><li><p><strong>Java 21 (2023 출시, 최신 LTS)</strong></p><ul><li>record 클래스, sealed 클래스, switch 표현식, 패턴 매칭 같은 새로운 문법을 제공한다.</li><li>Virtual Thread(Project Loom) 지원으로 동시성 프로그래밍 성능이 크게 개선되었다.</li><li>GC(가비지 컬렉터)도 발전하여 대규모 애플리케이션에서 더 효율적이다.</li></ul></li></ul><p>정리하면, <strong>Java 21은 Java 8의 모든 기능을 포함하고 추가 기능이 더 많다</strong>.<br>따라서 학습을 목적으로 한다면 최신 버전을 그대로 사용해도 문제가 없다.<br>다만 강의 예제 코드가 Java 8 기준이라면 화면이나 설치 과정에서 차이가 있을 수 있으니 주의할 필요가 있다.</p><p><br><br></p><h2 id="javac와-java-명령어-차이"><a href="#javac와-java-명령어-차이" class="headerlink" title="javac와 java 명령어 차이"></a>javac와 java 명령어 차이</h2><hr><p>자바 개발을 처음 배우면 <code>javac</code>와 <code>java</code> 명령어의 차이가 헷갈리곤 한다.</p><ul><li><p><strong>javac</strong></p><ul><li>Java Compiler의 줄임말이다.</li><li><code>.java</code> 소스 파일을 컴파일해서 <code>.class</code> 바이트코드 파일을 생성한다.</li><li>예: <code>javac Hello.java</code> → <code>Hello.class</code> 생성</li></ul></li><li><p><strong>java</strong></p><ul><li>JVM(Java Virtual Machine)을 실행하는 명령어이다.</li><li><code>.class</code> 파일을 읽어 실제 실행한다.</li><li>예: <code>java Hello</code> → <code>Hello.class</code> 실행</li></ul></li></ul><p>흐름을 정리하면, <code>javac</code>는 <strong>번역기</strong>, <code>java</code>는 <strong>낭독기</strong>라고 비유할 수 있다.<br>즉, 1) <code>javac</code>로 소스를 바이트코드로 변환하고, 2) <code>java</code>로 JVM에서 실행하는 구조이다.</p><p><br><br></p><h2 id="IDE-IntelliJ-에서-실행-버튼을-누르면"><a href="#IDE-IntelliJ-에서-실행-버튼을-누르면" class="headerlink" title="IDE(IntelliJ)에서 실행 버튼을 누르면?"></a>IDE(IntelliJ)에서 실행 버튼을 누르면?</h2><hr><p>많은 자바 개발자가 JetBrains의 <strong>IntelliJ IDEA</strong>를 사용한다.<br>여기서 실행 버튼(▶️)을 누르면 내부적으로는 <code>javac</code>와 <code>java</code>가 자동으로 실행된다.<br>하지만 단순히 명령어만 실행하는 것이 아니라, IDE는 프로젝트 설정에 따라 <strong>클래스패스(classpath)</strong>, <strong>라이브러리(JAR)</strong>, <strong>빌드 툴(Gradle/Maven)</strong> 설정까지 함께 관리한다.</p><p>즉, 개발자가 매번 명령어를 수동으로 입력하지 않아도 IDE가 대신 처리해주는 셈이다.<br>이 덕분에 코드 작성 → 빌드 → 실행 → 디버깅까지 한 번에 진행할 수 있다.</p><p><br><br></p><h2 id="빌드-시스템-Gradle-vs-Maven-vs-IntelliJ-Build"><a href="#빌드-시스템-Gradle-vs-Maven-vs-IntelliJ-Build" class="headerlink" title="빌드 시스템: Gradle vs Maven vs IntelliJ Build"></a>빌드 시스템: Gradle vs Maven vs IntelliJ Build</h2><hr><p>스프링 프로젝트를 진행하다 보면 빌드 시스템 선택이 필수적이다.</p><ul><li><strong>Gradle</strong><ul><li>Groovy 또는 Kotlin DSL 기반의 빌드 스크립트를 사용한다.</li><li>캐시와 병렬 빌드가 지원되어 속도가 빠르다.</li><li>최신 스프링 부트 프로젝트에서 기본적으로 권장된다.</li></ul></li></ul><blockquote><p><strong>Gradle DSL: Groovy vs Kotlin</strong></p><p>Gradle을 사용할 때는 <strong>빌드 스크립트를 어떤 언어로 작성할지</strong> 정해야 한다.<br>기본적으로 두 가지 DSL(Domain Specific Language)을 지원한다.</p><ul><li><p><strong>Groovy DSL (<code>build.gradle</code>)</strong></p><ul><li>전통적인 방식, 예제와 자료가 많다.</li><li>동적 타입 기반이라 문법이 유연하지만, 자동완성이나 에러 검출은 다소 약하다.</li></ul></li><li><p><strong>Kotlin DSL (<code>build.gradle.kts</code>)</strong></p><ul><li>정적 타입 기반이라 IDE 자동완성이 강력하다.</li><li>최근 Gradle과 스프링 공식 문서에서 적극적으로 권장되는 방식이다.</li><li>러닝 커브가 있지만, 유지보수성과 타입 안정성이 뛰어나다.<br>정리하면, <strong>새로운 스프링 부트 프로젝트</strong>를 시작한다면 <strong>Kotlin DSL</strong>을 추천한다.<br>다만, 강의나 기업 프로젝트에서 <strong>Groovy DSL</strong>을 사용한다면 그대로 따라가는 것도 문제없다. 빌드 결과는 동일하기 때문이다.</li></ul></li></ul></blockquote><p><br><br></p><ul><li><p><strong>Maven</strong></p><ul><li>XML 기반 빌드 설정을 사용한다.</li><li>표준화된 구조 덕분에 배우기 쉽고 예제와 문서가 많다.</li><li>보수적인 기업 프로젝트에서 여전히 널리 쓰인다.</li></ul></li><li><p><strong>IntelliJ Build</strong></p><ul><li>IDE 내부 전용 빌드 기능이다.</li><li>로컬에서 빠른 테스트에는 적합하지만, CI/CD 파이프라인이나 배포 환경에서는 한계가 있다.</li></ul></li></ul><p>정리하면, 개인 프로젝트나 최신 환경에서는 <strong>Gradle</strong>,<br>기업이나 강의에서 정해둔 환경이 있다면 <strong>Maven</strong>,<br>그리고 IDE 빌드는 <strong>보조 수단</strong> 정도로 쓰는 것이 가장 합리적이다.</p><p><br><br></p><h2 id="Java-21과-Virtual-Thread"><a href="#Java-21과-Virtual-Thread" class="headerlink" title="Java 21과 Virtual Thread"></a>Java 21과 Virtual Thread</h2><hr><p>스레드(Thread)는 자바 학습에서 중요한 주제다.<br>기존에는 플랫폼 스레드(운영체제 스레드) 기반으로 동작했지만, <strong>Java 21</strong>에서는 <strong>Virtual Thread(Project Loom)</strong> 가 정식 도입되었다.</p><ul><li><p><strong>플랫폼 스레드</strong></p><ul><li>운영체제 스레드와 1:1 매핑된다.</li><li>무겁고, 동시에 수천 개 이상 띄우기에는 한계가 있다.</li></ul></li><li><p><strong>가상 스레드 (Virtual Thread)</strong></p><ul><li>JVM이 관리하는 경량 스레드이다.</li><li>수십만 개를 띄워도 부담이 적어 <strong>블로킹 I/O 처리</strong>에 특히 유리하다.</li><li>코드 작성 방식은 기존 스레드와 거의 같지만, 성능상 이점이 크다.</li></ul></li></ul><p>예시 (Java 21):</p><pre class="language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">VtDemo</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token keyword">var</span> exec <span class="token operator">=</span> <span class="token class-name"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span>Executors</span><span class="token punctuation">.</span><span class="token function">newVirtualThreadPerTaskExecutor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> futures <span class="token operator">=</span> <span class="token class-name"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>stream<span class="token punctuation">.</span></span>IntStream</span><span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10_000</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">mapToObj</span><span class="token punctuation">(</span>i <span class="token operator">-></span> exec<span class="token punctuation">.</span><span class="token function">submit</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 블로킹 I/O 흉내</span> <span class="token keyword">return</span> i<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">toList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> f <span class="token operator">:</span> futures<span class="token punctuation">)</span> f<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"done"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><p><br><br></p><h2 id="마무리"><a href="#마무리" class="headerlink" title="마무리"></a>마무리</h2><hr><p>자바를 학습하다 보면 JDK 버전, javac/java 명령어, 빌드 툴 같은 개념이 따로따로 나와서 혼란스럽기 쉽다.<br>하지만 큰 그림을 먼저 이해하면 어떤 버전을 쓰든, 어떤 IDE를 쓰든 빠르게 적응할 수 있다.</p>]]></content>
<categories>
<category> 💻 Language </category>
</categories>
<tags>
<tag> Java </tag>
<tag> Spring </tag>
<tag> Gradle </tag>
<tag> Maven </tag>
<tag> IntelliJ </tag>
</tags>
</entry>
<entry>
<title>[HealthCare] HL7 v2 정리</title>
<link href="/2025/02/20/HealthCare/HL7/v2/"/>
<url>/2025/02/20/HealthCare/HL7/v2/</url>
<content type="html"><![CDATA[<h2 id="HL7-표준-정리-레거시부터-최신까지"><a href="#HL7-표준-정리-레거시부터-최신까지" class="headerlink" title="HL7 표준 정리: 레거시부터 최신까지"></a>HL7 표준 정리: 레거시부터 최신까지</h2><p>헬스데이터 스타트업이라면 반드시 알아야 할 국제 표준이 있다.<br>바로 <strong>HL7 (Health Level Seven)</strong> 이다.<br>이 글에서는 HL7 중 v2의 특징, 그리고 실제 메시지 구조까지 하나하나 정리해봤다.</p><hr><h3 id="1-HL7이란"><a href="#1-HL7이란" class="headerlink" title="1. HL7이란"></a>1. HL7이란</h3><ul><li>의료기관의 전산 시스템이 서로 정보를 주고받을 수 있게 해주는 <strong>국제 표준 언어</strong>이다.</li><li>환자 등록, 검사 결과, 처방, 영상 리포트, 보험 청구 등 모든 데이터 교환에 활용된다.</li><li>즉, 병원 간 “공통된 언어” 역할을 한다.</li></ul><hr><h3 id="2-HL7-v2-레거시지만-여전히-현역에서-활발히-사용-중"><a href="#2-HL7-v2-레거시지만-여전히-현역에서-활발히-사용-중" class="headerlink" title="2. HL7 v2 (레거시지만 여전히 현역에서 활발히 사용 중)"></a>2. HL7 v2 (레거시지만 여전히 현역에서 활발히 사용 중)</h3><p>HL7 v2는 1980년대부터 지금까지 가장 널리 사용되는 의료 데이터 교환 표준이다.<br>메시지는 여러 줄로 이루어져 있으며, 각 줄은 <strong>세그먼트(segment)</strong> 라고 부른다.<br>각 세그먼트는 <code>|</code> 기호로 구분된 필드들로 구성된다.</p><h4 id="1-HL7-v2-메시지의-큰-구조"><a href="#1-HL7-v2-메시지의-큰-구조" class="headerlink" title="1) HL7 v2 메시지의 큰 구조"></a>1) HL7 v2 메시지의 큰 구조</h4><p>대표적인 검사결과 메시지(ORU^R01)는 다음 순서로 세그먼트들이 온다.</p><pre class="language-text" data-language="text"><code class="language-text">MSH → PID → PV1 → OBR → OBX</code></pre><ul><li><strong>MSH (Message Header)</strong> : 메시지 전체의 헤더</li><li><strong>PID (Patient Identification)</strong> : 환자 정보</li><li><strong>PV1 (Patient Visit)</strong> : 입원/외래 방문 정보</li><li><strong>OBR (Observation Request)</strong> : 검사/관찰 요청 정보</li><li><strong>OBX (Observation Result)</strong> : 검사/관찰 결과</li></ul><blockquote><p>참고: 상황에 따라 ORC(주문 공통 정보), NTE(메모/주석) 같은 세그먼트가 추가될 수 있다.</p></blockquote><h4 id="2-주요-세그먼트-역할"><a href="#2-주요-세그먼트-역할" class="headerlink" title="2) 주요 세그먼트 역할"></a>2) 주요 세그먼트 역할</h4><table><thead><tr><th>세그먼트</th><th>풀네임</th><th>역할</th></tr></thead><tbody><tr><td><strong>MSH</strong></td><td>Message Header</td><td>메시지 메타데이터: 보낸/받는 시스템, 메시지 타입, 버전 등</td></tr><tr><td><strong>PID</strong></td><td>Patient Identification</td><td>환자 ID, 이름, 생년월일, 성별, 연락처</td></tr><tr><td><strong>PV1</strong></td><td>Patient Visit</td><td>입원/외래 방문 정보 (병동, 병실, 주치의, 방문 번호 등)</td></tr><tr><td><strong>ORC</strong></td><td>Common Order</td><td>검사/처방 등 주문에 대한 공통 정보</td></tr><tr><td><strong>OBR</strong></td><td>Observation Request</td><td>특정 검사 요청 정보 (검사명, 요청자, 시각 등)</td></tr><tr><td><strong>OBX</strong></td><td>Observation Result</td><td>실제 검사/관찰 결과 값</td></tr><tr><td><strong>NTE</strong></td><td>Notes</td><td>주석이나 부가 설명</td></tr></tbody></table><h4 id="3-실제-예시-혈압-검사-결과-메시지"><a href="#3-실제-예시-혈압-검사-결과-메시지" class="headerlink" title="3) 실제 예시: 혈압 검사 결과 메시지"></a>3) 실제 예시: 혈압 검사 결과 메시지</h4><pre class="language-text" data-language="text"><code class="language-text">MSH|^~\&|HIS|HOSP_A|LAB|HOSP_B|202508261030||ORU^R01|MSG0001|P|2.9PID|1||12345^^^HOSP_A||Kim^Minjun||19900101|M|||Seoul^^KR||010-1234-5678PV1|1|I|WARD^101^1^HOSP_A||||1234^Lee^DoctorOBR|1|||BP^Blood Pressure^LN|||202508261030OBX|1|NM|8480-6^Systolic BP^LN||120|mm[Hg]|90-140|N|||F||202508261030OBX|2|NM|8462-4^Diastolic BP^LN||80|mm[Hg]|60-90|N|||F||202508261030</code></pre><h4 id="4-세그먼트별-해설"><a href="#4-세그먼트별-해설" class="headerlink" title="4) 세그먼트별 해설"></a>4) 세그먼트별 해설</h4><ul><li><p><strong>1) MSH (Message Header)</strong>: <code>MSH|^~\&|HIS|HOSP_A|LAB|HOSP_B|202508261030||ORU^R01|MSG0001|P|2.9</code></p><ul><li><code>HIS</code> = 보내는 애플리케이션 (병원 전산 시스템, HIS)</li><li><code>^~\&</code> = 데이터를 어떻게 나눠야 하는지 알려주는 구분자.</li><li><code>HOSP_A</code> = 보내는 기관 (병원 A)</li><li><code>LAB</code> = 받는 애플리케이션 (검사실 시스템)</li><li><code>HOSP_B</code> = 받는 기관 (병원 B)</li><li><code>202508261030</code> = 메시지 생성 시각</li><li><code>ORU^R01</code> = 메시지 타입 (Observation Result, 검사 결과 보고)</li><li><code>MSG0001</code> = 메시지 컨트롤 ID (메시지 고유 식별자)</li><li><code>P</code> = 운영 모드 (Production)</li><li><code>2.9</code> = HL7 버전</li></ul></li><li><p><strong>PID (Patient Identification, 환자 정보)</strong>: <code>PID|1||12345^^^HOSP_A||Kim^Minjun||19900101|M|||Seoul^^KR||010-1234-5678</code></p><ul><li><code>12345^^^HOSP_A</code> = 환자 ID(12345), 발급 기관=HOSP_A</li><li><code>Kim^Minjun</code> = 환자 이름 (성=Kim, 이름=Minjun)</li><li><code>19900101</code> = 환자 생년월일 (1990년 01월 01일)</li><li><code>M</code> = 성별 (Male, 남자)</li><li><code>Seoul^^KR</code> = 주소 (서울, 대한민국)</li><li><code>010-1234-5678</code> = 환자 연락처</li></ul></li><li><p><strong>PV1 (Patient Visit, 환자 방문 정보)</strong>: <code>PV1|1|I|WARD^101^1^HOSP_A||||1234^Lee^Doctor</code></p><ul><li><code>I</code> = 방문 유형 (Inpatient, 입원)</li><li><code>WARD^101</code> = 병동/병실 (101호)</li><li><code>Lee^Doctor</code> = 담당 의사</li></ul></li><li><p><strong>OBR (Observation Request, 검사 요청)</strong>: <code>OBR|1|||BP^Blood Pressure^LN|||202508261030</code></p><ul><li><code>BP^Blood Pressure^LN</code> = 검사 항목: 혈압 (코드=BP, 표시명=Blood Pressure, 코드체계=LOINC: LN)</li><li><code>202508261030</code> = 검사 요청 시각</li></ul></li><li><p><strong>OBX (Observation Result, 검사 결과) - 수축기 혈압</strong>: <code>OBX|1|NM|8480-6^Systolic BP^LN||120|mm[Hg]|90-140|N|||F||202508261030</code></p><ul><li><code>NM</code> = Numeric, 값은 숫자</li><li><code>8480-6^Systolic BP^LN</code> = 수축기 혈압, LOINC 코드 8480-6</li><li><code>120</code> = 측정값</li><li><code>mm[Hg]</code> = 단위 (밀리미터 수은주)</li><li><code>90-140</code> = 참고 범위</li><li><code>N</code> = 정상 (Normal)</li><li><code>F</code> = Final (최종 결과)</li><li><code>202508261030</code> = 결과 생성 시각</li></ul></li><li><p><strong>OBX (Observation Result, 검사 결과) - 이완기 혈압</strong>: <code>OBX|2|NM|8462-4^Diastolic BP^LN||80|mm[Hg]|60-90|N|||F||202508261030</code></p><ul><li><code>NM</code> = Numeric</li><li><code>8462-4^Diastolic BP^LN</code> = 이완기 혈압, LOINC 코드 8462-4</li><li><code>80</code> = 측정값</li><li><code>mm[Hg]</code> = 단위</li><li><code>60-90</code> = 참고 범위</li><li><code>N</code> = 정상</li><li><code>F</code> = Final (최종 결과)</li><li><code>202508261030</code> = 결과 생성 시각</li></ul></li></ul><hr><h3 id="HL7-v2-요약"><a href="#HL7-v2-요약" class="headerlink" title="HL7 v2 요약"></a>HL7 v2 요약</h3><ul><li>HL7 v2 메시지는 여러 세그먼트로 구성된 텍스트 메시지이다.</li><li><strong>MSH → PID → PV1 → OBR → OBX</strong> 순으로<br>환자 정보, 방문 정보, 검사 요청, 검사 결과가 기록된다.</li><li>OBX는 실제 결과 값을 담는 부분일 뿐, 항상 <strong>앞뒤 세그먼트와 함께 묶여서</strong> 온다.</li><li>이 구조를 이해하면 HL7 메시지를 해석하고, 병원 EHR 시스템에 맞게 매핑할 수 있다.</li></ul>]]></content>
<categories>
<category> 🏥 HealthCare </category>
</categories>
<tags>
<tag> EHR </tag>
<tag> HL7 </tag>
<tag> SDC </tag>
<tag> HealthCare </tag>
</tags>
</entry>
<entry>
<title>[HealthCare] EHR 연동: HL7 / IEEE 11073 SDC 총정리</title>
<link href="/2025/01/10/HealthCare/EHR-FHIR-SDC/standard/"/>
<url>/2025/01/10/HealthCare/EHR-FHIR-SDC/standard/</url>
<content type="html"><![CDATA[<h2 id="EHR-연동-HL7-IEEE-11073-SDC-올인원-정리"><a href="#EHR-연동-HL7-IEEE-11073-SDC-올인원-정리" class="headerlink" title="EHR 연동: HL7 / IEEE 11073 SDC 올인원 정리"></a>EHR 연동: HL7 / IEEE 11073 SDC 올인원 정리</h2><p>나는 이래봬도 의료데이터를 활용해서 개발하는 사람인데, 최근 부트캠프 멘토링을 하다보면 의료기술 쪽으로 프로그램이 많이 생기길래 정리해두면 좋겠다 싶어 글을 작성해봤다.</p><p>의료데이터 스타트업을 운영하거나, 병원과 시스템을 연동하려 한다면 반드시 알아야 할 것이 있다.<br>바로 <strong>HL7</strong>과 <strong>IEEE 11073 SDC</strong>이다. 이 글에서는 두 표준이 무엇인지, 어떻게 쓰이는지, 그리고 최신 동향까지 한 번에 정리하였다.</p><hr><h3 id="1-EHR이란-무엇인가"><a href="#1-EHR이란-무엇인가" class="headerlink" title="1. EHR이란 무엇인가"></a>1. EHR이란 무엇인가</h3><ul><li><strong>EHR (Electronic Health Record)</strong> 는 전자의무기록 시스템을 말한다.</li><li>환자의 진료기록, 검사결과, 처방, 영상 등 모든 의료 데이터를 담는다.</li><li>문제는 병원마다 시스템과 기기가 제각각이라서 서로 데이터를 주고받기 어렵다는 점이다.</li><li>그래서 <strong>국제 표준 언어</strong>가 필요하게 되었다. 이게 바로 HL7의 탄생 계기이다.</li></ul><hr><h3 id="2-HL7-병원-시스템-간-언어"><a href="#2-HL7-병원-시스템-간-언어" class="headerlink" title="2. HL7: 병원 시스템 간 언어"></a>2. HL7: 병원 시스템 간 언어</h3><ul><li><strong>HL7 (Health Level Seven)</strong> 은 병원 전산끼리 정보를 주고받는 국제 표준이다.</li><li><strong>HL7 v2</strong>는 1980년대부터 쓰였고, 여전히 대부분의 병원에서 사용한다.</li><li><strong>HL7 v3</strong>는 XML 기반으로 복잡하여 널리 퍼지지 못했다.</li><li><strong>FHIR (Fast Healthcare Interoperability Resources)</strong> 는 최신 표준으로 JSON과 API 기반이라 현대적인 시스템과 잘 맞는다.</li><li>요약하자면 <strong>옛날에는 텍스트, 중간에는 XML, 지금은 JSON(FHIR)</strong> 시대라고 할 수 있다.</li></ul><hr><h3 id="3-IEEE-11073-SDC-의료기기-연결-언어"><a href="#3-IEEE-11073-SDC-의료기기-연결-언어" class="headerlink" title="3. IEEE 11073 SDC: 의료기기 연결 언어"></a>3. IEEE 11073 SDC: 의료기기 연결 언어</h3><ul><li><strong>SDC (Service-oriented Device Connectivity)</strong> 는 의료기기를 네트워크에 연결하기 위한 국제 표준이다.</li><li>의료기기가 “나는 혈압 데이터를 제공한다” “나는 산소포화도를 알려줄 수 있다”와 같이 스스로 기능을 설명할 수 있게 정의하였다.</li><li>환자 모니터, 인퓨전 펌프, 수술 장비 등 다양한 기기가 SDC를 통해 안전하게 데이터를 주고받는다.</li><li>단순한 측정값 전송뿐 아니라 알림, 원격 제어까지 지원한다.</li><li>보안은 <strong>TLS와 X.509 인증서</strong>를 통해 이루어진다.</li></ul><hr><h3 id="4-HL7-SDC-조합-왜-필요한가"><a href="#4-HL7-SDC-조합-왜-필요한가" class="headerlink" title="4. HL7 + SDC 조합: 왜 필요한가"></a>4. HL7 + SDC 조합: 왜 필요한가</h3><ul><li>병원에는 수많은 기기가 있고, 각각이 데이터를 쏟아낸다.</li><li>각 병원마다 데이터 세팅이 다르기 때문에, 이 데이터를 EHR에 직접 연결하면 포맷 충돌과 오류가 발생한다. 그래서 중간에서 standard 구조로 리팩토링을 해줘야 알아서들 편하게 데이터를 다룰 수 있다.</li><li>그래서 <strong>구조는 이렇게 나뉜다</strong>:<pre class="language-text" data-language="text"><code class="language-text">[의료기기] --SDC--> [게이트웨이] --HL7/FHIR--> [EHR]</code></pre><ul><li>의료기기: 혈압 120/80 mmHg 데이터를 생성한다.</li><li>SDC 게이트웨이: 이를 SDC 언어로 받아 HL7/FHIR로 변환한다.</li><li>EHR: 변환된 데이터를 환자 기록에 저장한다.</li></ul></li></ul><hr><h3 id="5-실제-데이터-흐름-예시"><a href="#5-실제-데이터-흐름-예시" class="headerlink" title="5. 실제 데이터 흐름 예시"></a>5. 실제 데이터 흐름 예시</h3><h4 id="1-SDC에서-나온-값"><a href="#1-SDC에서-나온-값" class="headerlink" title="1) SDC에서 나온 값"></a>1) SDC에서 나온 값</h4><pre class="language-text" data-language="text"><code class="language-text">Metric: BloodPressureValue: 120/80Unit: mmHg</code></pre><h4 id="2-게이트웨이가-변환한-FHIR-JSON"><a href="#2-게이트웨이가-변환한-FHIR-JSON" class="headerlink" title="2) 게이트웨이가 변환한 FHIR(JSON)"></a>2) 게이트웨이가 변환한 FHIR(JSON)</h4><pre class="language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"resourceType"</span><span class="token operator">:</span> <span class="token string">"Observation"</span><span class="token punctuation">,</span> <span class="token property">"subject"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"reference"</span><span class="token operator">:</span> <span class="token string">"Patient/12345"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"device"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"reference"</span><span class="token operator">:</span> <span class="token string">"Device/dev-mds-01"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"effectiveDateTime"</span><span class="token operator">:</span> <span class="token string">"2025-08-26T10:30:00+09:00"</span><span class="token punctuation">,</span> <span class="token property">"status"</span><span class="token operator">:</span> <span class="token string">"final"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"coding"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://loinc.org"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"85354-9"</span><span class="token punctuation">,</span> <span class="token property">"display"</span><span class="token operator">:</span> <span class="token string">"Blood pressure panel"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"component"</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"coding"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://loinc.org"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"8480-6"</span><span class="token punctuation">,</span> <span class="token property">"display"</span><span class="token operator">:</span> <span class="token string">"Systolic blood pressure"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"valueQuantity"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"mmHg"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"coding"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token property">"system"</span><span class="token operator">:</span> <span class="token string">"http://loinc.org"</span><span class="token punctuation">,</span> <span class="token property">"code"</span><span class="token operator">:</span> <span class="token string">"8462-4"</span><span class="token punctuation">,</span> <span class="token property">"display"</span><span class="token operator">:</span> <span class="token string">"Diastolic blood pressure"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"valueQuantity"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">80</span><span class="token punctuation">,</span> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"mmHg"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span><span class="token punctuation">}</span></code></pre><hr><h3 id="6-최신-버전-기준"><a href="#6-최신-버전-기준" class="headerlink" title="6. 최신 버전 기준"></a>6. 최신 버전 기준</h3><ul><li><strong>HL7 v2:</strong> 2.9.1 (2024)</li><li><strong>FHIR:</strong> R5 (2023, 최신 안정판)</li><li><strong>IEEE 11073 SDC:</strong><ul><li>10207 (BICEPS, 데이터 모델)</li><li>20702 (MDPWS, 통신)</li><li>20701 (아키텍처 바인딩)</li></ul></li></ul><hr><h3 id="7-정리"><a href="#7-정리" class="headerlink" title="7. 정리"></a>7. 정리</h3><ul><li><strong>HL7/FHIR</strong>은 병원 전산끼리 소통하는 언어이다.</li><li><strong>SDC</strong>는 의료기기끼리 소통하는 언어이다.</li><li><strong>게이트웨이</strong>는 두 언어를 번역한다.</li><li>최신 연동은 <strong>SDC + FHIR</strong> 조합으로 한다.</li><li>다만 현실적으로 병원 내부에는 <strong>HL7 v2</strong>도 병행해야 한다.</li><li>이 구조를 이해하면 헬스데이터 스타트업이 병원과 연동하는 모든 흐름을 설계할 수 있다.</li></ul>]]></content>
<categories>
<category> 🏥 HealthCare </category>
</categories>
<tags>
<tag> EHR </tag>
<tag> HL7 </tag>
<tag> IEEE </tag>
<tag> SDC </tag>
<tag> HealthCare </tag>
</tags>
</entry>
<entry>
<title>[Vue.js] Vue3 SEO 최적화하기 (sitemap.map, robots.txt)</title>
<link href="/2025/01/01/Frontend/Vuejs/SEO/"/>
<url>/2025/01/01/Frontend/Vuejs/SEO/</url>
<content type="html"><![CDATA[<p>개발 포트폴리오 사이트를 만들었는데, 배포는 성공했다만 생각해보니 검색 유입을 위한 SEO(Search Engine Optimization) 최적화를 하지 않았음을 깨달았다..<br>따라서 Vue3로 하는 방법을 간략히 정리해보고자 한다.</p><p>참고로 여기서는 각 포털별 세팅은 다루지 않고 <code>sitemap.xml</code>, <code>robots.txt</code> 생성법만 작성했다.<br>포털별 세팅은 인터넷에 검색하면 많이 나오기 때문에 우리 개발 알잘딱 분들은 잘 하실거라 믿는다 ㅎㅎ</p><h2 id="sitemap-xml만들기"><a href="#sitemap-xml만들기" class="headerlink" title="sitemap.xml만들기"></a>sitemap.xml만들기</h2><p>일단 자신의 프로젝트에서 어떤 vue 프레임워크를 사용하는지 잘 알아야 한다.<br>VuePress같은 경우에는 <code>vuepress-plugin-sitemap</code> 패키지를 설치해서 module.exports를 하면 바로되지만,<br>필자는 vue3를 쌩으로 갖다 박았기 때문에 직접 생성을 해야한다. ㅎㅋ… <del>뒤늦게 몰려드는 후회</del><br>왜냐면 플러그인 패키지를 돌리면 아래와 같은 에러가 발생하기 때문이다.</p><pre class="language-bash" data-language="bash"><code class="language-bash">node:internal/modules/esm/get_format:183 throw new ERR_UNKNOWN_FILE_EXTENSION<span class="token punctuation">(</span>ext, filepath<span class="token punctuation">)</span><span class="token punctuation">;</span> ^TypeError <span class="token punctuation">[</span>ERR_UNKNOWN_FILE_EXTENSION<span class="token punctuation">]</span>: Unknown <span class="token function">file</span> extension <span class="token string">".vue"</span> <span class="token keyword">for</span> /Users/rubykim/portfolio/frontend/src/views/Home.vue at Object.getFileProtocolModuleFormat <span class="token punctuation">[</span>as file:<span class="token punctuation">]</span> <span class="token punctuation">(</span>node:internal/modules/esm/get_format:183:9<span class="token punctuation">)</span> at defaultGetFormat <span class="token punctuation">(</span>node:internal/modules/esm/get_format:209:36<span class="token punctuation">)</span> at defaultLoadSync <span class="token punctuation">(</span>node:internal/modules/esm/load:173:14<span class="token punctuation">)</span> at <span class="token comment">#loadAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:723:12)</span> at <span class="token comment">#loadSync (node:internal/modules/esm/loader:745:49)</span> at ModuleLoader.getModuleJobForRequire <span class="token punctuation">(</span>node:internal/modules/esm/loader:384:38<span class="token punctuation">)</span> at new ModuleJobSync <span class="token punctuation">(</span>node:internal/modules/esm/module_job:342:34<span class="token punctuation">)</span> at ModuleLoader.importSyncForRequire <span class="token punctuation">(</span>node:internal/modules/esm/loader:332:11<span class="token punctuation">)</span> at loadESMFromCJS <span class="token punctuation">(</span>node:internal/modules/cjs/loader:1570:24<span class="token punctuation">)</span> at Module._compile <span class="token punctuation">(</span>node:internal/modules/cjs/loader:1722:5<span class="token punctuation">)</span> <span class="token punctuation">{</span> code: <span class="token string">'ERR_UNKNOWN_FILE_EXTENSION'</span><span class="token punctuation">}</span></code></pre><br><p>한줄 요약하자면 <strong>vue파일 인식이 안되네요</strong>라는 말이므로, 결국 노가다를 해야한다는 뜻이다.<br>다행히도 노가다 코드는 매우 간단한 편이다.</p><p>먼저 패키지를 설치해준 후,</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> sitemap</code></pre><br><p>코드를 다음과 같이 세팅해주면 된다.</p><pre class="language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">// generate-sitemap.js</span><span class="token keyword">const</span> <span class="token punctuation">{</span> SitemapStream<span class="token punctuation">,</span> streamToPromise <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"sitemap"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> <span class="token punctuation">{</span> createWriteStream <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"fs"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> <span class="token punctuation">{</span> resolve <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"path"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token comment">// 여기에서 모든 page의 url입력</span> <span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">1.0</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">"/about"</span><span class="token punctuation">,</span> <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">0.9</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">"/contact"</span><span class="token punctuation">,</span> <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">0.9</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">const</span> sitemap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SitemapStream</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">hostname</span><span class="token operator">:</span> <span class="token operator"><</span>실제_배포_사이트_url<span class="token operator">></span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">streamToPromise</span><span class="token punctuation">(</span> routes <span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">stream<span class="token punctuation">,</span> route</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> stream<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">url</span><span class="token operator">:</span> route<span class="token punctuation">.</span>path<span class="token punctuation">,</span> <span class="token literal-property property">changefreq</span><span class="token operator">:</span> <span class="token string">"monthly"</span><span class="token punctuation">,</span> <span class="token literal-property property">priority</span><span class="token operator">:</span> route<span class="token punctuation">.</span>priority<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> stream<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> sitemap<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> outputPath <span class="token operator">=</span> <span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">"dist/sitemap.xml"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> writeStream <span class="token operator">=</span> <span class="token function">createWriteStream</span><span class="token punctuation">(</span>outputPath<span class="token punctuation">)</span><span class="token punctuation">;</span> writeStream<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> writeStream<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><br><p>이 파일을 설정해줬다면 <code>package.json</code>의 build script에 sitemap 생성 명령어를 같이 입력해주자.</p><pre class="language-json" data-language="json"><code class="language-json">...<span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"vue-cli-service build && node generate-sitemap.js"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>...</code></pre><p><br><br><br></p><h2 id="robots-txt-만들기"><a href="#robots-txt-만들기" class="headerlink" title="robots.txt 만들기"></a>robots.txt 만들기</h2><p>robots.txt는 만들기 매우 쉽다. 그냥 txt 파일을 갖다 넣으면 되기 때문이다.<br>다만 여기서 중요한 것이 있는데, <strong>웹사이트 루트 디렉토리</strong>에 위치해야 한다는 것이다.</p><p>특별한 프로젝트 구조가 아닌 이상, 대부분 <code>public</code>폴더에 robots.txt를 만들어놓으면 build시 자동으로 루트 디렉토리에 해당 파일이 위치하게 될 것이다.<br>아래는 robots.txt 예시이다.</p><pre class="language-txt" data-language="txt"><code class="language-txt">User-agent: *Disallow:Sitemap: <실제_배포_사이트_url>/sitemap.xml</code></pre><p>필자는 포트폴리오 사이트에 대해서 SEO를 설정하고 있기 때문에, Disallow를 따로 세팅하지는 않았다.<br>만약 개발 중인 페이지나 검색 결과에 나타나기 원하지 않는 콘텐츠가 있다면, Disallow에 넣어주도록 하자.</p><p><br><br><br></p><h2 id="결과물"><a href="#결과물" class="headerlink" title="결과물"></a>결과물</h2><img src="./1.png" width="800"><img src="./2.png" width="800">]]></content>
<categories>
<category> ✨ Frontend </category>
</categories>
<tags>
<tag> Javascript </tag>
<tag> Frontend </tag>
<tag> Vue </tag>
<tag> SEO </tag>
</tags>
</entry>
<entry>
<title>[Next.js] 배포하기: AWS Amplify vs Github pages vs fly.io vs Vercel vs Netlify</title>
<link href="/2024/12/29/Frontend/Nextjs/Deployment/"/>
<url>/2024/12/29/Frontend/Nextjs/Deployment/</url>
<content type="html"><![CDATA[<p>프로젝트를 하다보면 언젠가 배포를 해야 하는 순간이 온다.</p><p>필자 또한 최근 nextJS로 간단한 홈페이지를 만들었는데, 막상 배포를 하려니 nextJS라 고려해야할 점이 많아서 한 번 정리해보고자 한다.</p><p>결론만 볼 사람들은 5번으로 바로 넘어가면 된다.</p><h2 id="NextJS를-선택한-이유는"><a href="#NextJS를-선택한-이유는" class="headerlink" title="(NextJS를 선택한 이유는?)"></a>(NextJS를 선택한 이유는?)</h2><p>일단 플랫폼을 비교하기 앞서서 굳이 nextJS를 선택한 이유를 간단히 적어보자 한다.<br>그래야 비교하는 의미가 있기 때문이다.</p><p>NextJS는 <code>SSR(서버 사이드 랜더링)</code>을 목적으로 주로 선택되는 웹 프레임워크이다.<br>즉 각 페이지를 서버 측에서 먼저 렌더링을 할 수 있기 때문에, 초기 페이지 로딩 속도가 빠르고 <code>SEO(검색 엔진 최적화)</code>에 매우 유리하다.<br>필자 또한 만든 프로그램이 인터넷 검색이 잘 되도록 하고 싶어서, 고민없이 바로 nextJS를 선택했다.</p><h2 id="1-❌-AWS-Amplify"><a href="#1-❌-AWS-Amplify" class="headerlink" title="1. ❌ AWS Amplify"></a>1. ❌ AWS Amplify</h2><img src="./1.png" width="800"><p>필자는 회사 업무로 인해 AWS를 주로 사용하는데, 생각해보니 AWS CloudFront를 직접 설정해서 프론트엔드를 배포했지, Amplify는 써본 적이 없어서 연습 겸 여기로 호스팅을 시도를 했다.</p><p>물론 어찌저찌 하니 배포는 잘 됐는데, 문제가 발생했다.<br>크게 2가지 문제가 있었는데, 바로 <code>배포/반영 시간이 너무 걸린다</code>는 것이다.</p><p>Amplify는 CDN을 위해 CloudFront를 사용하는데, CloudFront는 CDN과 Compute@Edge 배포에서 invalidations(무효화)와 propagations(전파)를 지원하지 않아 일종의 <strong>병목현상</strong>이 나타난 것이었다.</p><p>필자가 구현한 프로젝트가 간단한 편에 속하는데, 실제로 배포 후 반영까지 약 20분이라는 시간이 걸렸다. 실로 놀라울 따름…</p><br><p>또 하나의 문제는 <code>이미지 로드가 느리다는 것</code>이었다.<br>현재 기준으로 region을 <strong>us-east-1</strong>(버지니아 북부)로만 설정할 수 있는데, 파일 내부 static 이미지들이 캐싱되기 전까지는 이미지 로드가 매우 느리다는 것이었다.</p><p>맨 처음에 이미지 로딩 시간이 너무 길어보여서 착각한건가 싶어 몇 번 재테스트를 했는데,<br>그럼에도 불구하고 확실하게 정적 이미지들이 캐싱되기 전까지는 느리게 불러와지는 모습에 한탄을 금치 못했다.</p><p>눈물 머금고 나의 작고 귀여운 돈을 바치겠다는데 이렇게까지 느릴 일이 있나? 싶어서 결국 던지게 되었다 ㅎㅎ</p><p><br><br></p><h2 id="2-❌-Github-Pages"><a href="#2-❌-Github-Pages" class="headerlink" title="2. ❌ Github Pages"></a>2. ❌ Github Pages</h2><img src="./2.jpg" width="800"><p>사실 이거는 시도조차 안했다. 왜냐면 이 블로그 자체가 Github Pages로 운영되고 있기 때문에 굳이 동일한 기술 스택을 사용해야 하나 싶었기 때문이다.</p><p>무엇보다 Github Pages를 포기한 이유가 바로 <code>API 라우트</code> 였다. 안타깝게도 <strong>Github Pages는 서버 이용이 불가능</strong>한데, 나는 API 라우트를 구현했기 때문이다. 서버 없이 어떻게 작동시키겠어…ㅠㅜ</p><p>따라서 API 라우트 없는 build 결과물로는 github pages 배포는 가능하지만, API 라우트를 사용한다면 100% github pages에서 서비스가 안돌아가기 때문이 다른 대안점을 찾아야 했다.</p><p><br><br></p><h2 id="3-✅-fly-io"><a href="#3-✅-fly-io" class="headerlink" title="3. ✅ fly.io"></a>3. ✅ fly.io</h2><img src="./3.png" width="800"><p><a href="https://fly.io/">fly.io</a>는 컨테이너 기반 애플리케이션으로, 전 세계 여러 데이터 센터에서 애플리케이션을 자동으로 배포하여 지연 시간을 최소화 및 성능 최적화가 가능한 플랫폼이다.</p><p>클라우드에서 Container Registry에 이미지를 업로드하여 Kubernetes로 돌리는건 흔한 경우라, 이것도 체험을 해보고 싶어서 가볍게 업로드를 해봤다.</p><p>명령어를 써야한대서 어렵나 싶었는데, 생각보다 나쁘지 않았다. 아래 명령어가 전부이기 때문이다.<br>하지만 이 프로그램은 아쉽게도 최종 후보에서 탈락하고 말았다. 왜냐면 무료기간 이후로 매달 25달러를 내야하기 때문이다 <del>돈아끼기</del></p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 1. flyctl 설치</span><span class="token comment">## brew 설치</span>brew <span class="token function">install</span> flyctl<span class="token comment">## brew사용이 어려울 시</span><span class="token function">curl</span> <span class="token parameter variable">-L</span> https://fly.io/install.sh <span class="token operator">|</span> <span class="token function">sh</span><span class="token comment"># 2. flyctl 로그인</span>flyctl auth login<span class="token comment"># 3. 초기 설정 및 배포하기</span><span class="token comment">## 이때 fly.toml 및 Dockerfile이 생성되며, 자동으로 배포가 진행됨</span>fly launch<span class="token comment"># 4. 이후 코드 수정 후 배포 업데이트</span>fly deploy</code></pre><p><br><br></p><h2 id="4-✅-Vercel-vs-Netlify"><a href="#4-✅-Vercel-vs-Netlify" class="headerlink" title="4. ✅ Vercel vs Netlify"></a>4. ✅ Vercel vs Netlify</h2><img src="./4.avif" width="800"><p>이 두 친구들은 프론트엔드 배포를 하는데 많이 사용되는 플랫폼이다.<br>왜냐면 배포도 매우 쉽고 무엇보다 <strong>무료</strong> 범위 내에서 충분히 커버할 수 있기 때문이다.</p><p>따라서 이 둘을 두고 어디로 배포를 하냐 의견이 분분히 발생하는데, 간단하게 정리를 해보고자 한다.<br>(참고: <a href="https://ikius.com/blog/vercel-vs-netlify">https://ikius.com/blog/vercel-vs-netlify</a>)</p><h4 id="Vercel"><a href="#Vercel" class="headerlink" title="Vercel"></a>Vercel</h4><ul><li>nextJS에 한정해서 최적의 배포가 가능: vercel에서 nextjs를 만들었기 때문</li><li>빠른 로딩 속도</li></ul><h4 id="Netlify"><a href="#Netlify" class="headerlink" title="Netlify"></a>Netlify</h4><ul><li>다양한 정적 사이트 생성기 & 복잡한 배포 요구사항이 있을 경우에 사용이 좋음: vercel보다 플러그인이 많이 제공되기 때문</li><li>무료 플랜에서 상업 용도로 사용 가능 (vercel은 상업용으로 페이지 만들 시 플랜 구매 필요)</li></ul><p><br><br></p><h2 id="5-결론"><a href="#5-결론" class="headerlink" title="5. 결론"></a>5. 결론</h2><p>합리적인 가격(무료 또는 소액)으로 배포를 한다는 가정하에,<br><code>Vercel</code>, <code>Netlify</code>, <code>fly.io</code>를 추천한다!</p><p>Github Pages는 NextJS에서 API 라우트를 구현하지 않았을 때만 추천한다.</p>]]></content>
<categories>
<category> ✨ Frontend </category>
</categories>
<tags>
<tag> NextJS </tag>
<tag> Javascript </tag>
<tag> Frontend </tag>
</tags>
</entry>
<entry>
<title>[GCP] GCP에서 사용자 인증정보(OAuth)를 사용해 Access Token 받아오기</title>
<link href="/2024/12/20/Cloud/GCP/Console/Oauth2/"/>
<url>/2024/12/20/Cloud/GCP/Console/Oauth2/</url>
<content type="html"><![CDATA[<p>Google Calendar에 커피챗 일정을 잡아주는 프로그램을 만드는 중인데, 이 과정에서 Google Calendar API를 사용하기 위해서는 GCP로부터 Access Token 발급을 받아야 하므로 그 방법을 한 번 정리해보자고 한다.<br><code>scope</code>부분만 원하는 서비스로 선택하면 해당 서비스의 Access Token을 받을 수 있으며 여기서는 내가 작업 중인 Google Calendar를 예시로 글을 작성해본다.</p><h2 id="1-GCP-API-및-서비스-사용자-인증-정보에서-OAuth-client-생성"><a href="#1-GCP-API-및-서비스-사용자-인증-정보에서-OAuth-client-생성" class="headerlink" title="1. GCP > API 및 서비스 > 사용자 인증 정보에서 OAuth client 생성"></a>1. GCP > API 및 서비스 > 사용자 인증 정보에서 OAuth client 생성</h2><img src="./1.png" width="800"><p>이번 포스트에서는 클라이언트 생성 방법보다는 Access Token발급을 중점으로 다루고 있기 때문에 client 세팅 방법은 따로 안내하지 않겠다.</p><p>여기에서 우리가 확인해야 할 정보는 다음과 같다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token assign-left variable">GOOGLE_CLIENT_ID</span><span class="token operator">=</span><span class="token operator"><</span>클라이언트 ID<span class="token operator">></span><span class="token assign-left variable">GOOGLE_CLIENT_SECRET</span><span class="token operator">=</span><span class="token operator"><</span>클라이언트 보안 비밀번호<span class="token operator">></span><span class="token assign-left variable">GOOGLE_REDIRECT_URI</span><span class="token operator">=</span><span class="token operator"><</span>승인된 리디렉션 URI주소<span class="token operator">></span></code></pre><p><br><br></p><h2 id="2-Google-Calender의-Access-Token요청을-위한-URL-입력"><a href="#2-Google-Calender의-Access-Token요청을-위한-URL-입력" class="headerlink" title="2. Google Calender의 Access Token요청을 위한 URL 입력"></a>2. Google Calender의 Access Token요청을 위한 URL 입력</h2><p>아무 인터넷 창에다가 다음의 url을 입력한다.</p><pre class="language-bash" data-language="bash"><code class="language-bash">https://accounts.google.com/o/oauth2/v2/auth?scope<span class="token operator">=</span>https://www.googleapis.com/auth/calendar<span class="token operator">&</span><span class="token assign-left variable">access_type</span><span class="token operator">=</span>offline<span class="token operator">&</span><span class="token assign-left variable">include_granted_scopes</span><span class="token operator">=</span>true<span class="token operator">&</span><span class="token assign-left variable">response_type</span><span class="token operator">=</span>code<span class="token operator">&</span><span class="token assign-left variable">redirect_uri</span><span class="token operator">=</span><span class="token operator"><</span>GOOGLE_REDIRECT_URI<span class="token operator">>&</span><span class="token assign-left variable">client_id</span><span class="token operator">=</span><span class="token operator"><</span>GOOGLE_CLIENT_ID<span class="token operator">></span></code></pre><img src="./2.png" width="800"><img src="./3.png" width="800"><p>값이 잘 입력됐다면 google 계정으로 로그인이 나오며, 액세스 허용 여부를 물어보는 페이지를 볼 수 있다.</p><p><br><br></p><h2 id="3-Redirect-uri로-받아온-값-확인"><a href="#3-Redirect-uri로-받아온-값-확인" class="headerlink" title="3. Redirect uri로 받아온 값 확인"></a>3. Redirect uri로 받아온 값 확인</h2><p>위 이미지에서 “허용” 버튼을 누르면 redirect uri로 바로 이동한다.<br>이 때 주소창을 확인하면 response를 확인할 수 있으며, <code>CODE</code>부분을 잘 백업해두자.<br>코드의 유효시간은 10분인 것 또한 참고할 것</p><pre class="language-bash" data-language="bash"><code class="language-bash">GOOGLE_REDIRECT_URI/?code<span class="token operator">=</span><span class="token operator"><</span>CODE<span class="token operator">>&</span><span class="token assign-left variable">scope</span><span class="token operator">=</span>https://www.googleapis.com/auth/calendar</code></pre><h2 id="4-Access-Token-발급받기"><a href="#4-Access-Token-발급받기" class="headerlink" title="4. Access Token 발급받기"></a>4. Access Token 발급받기</h2><p>이제 Access Token을 발급받을 준비가 되었다.<br>다음의 명령어를 입력해보자. curl이니 터미널에 입력하면 된다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">--request</span> POST <span class="token punctuation">\</span> <span class="token parameter variable">--url</span> https://oauth2.googleapis.com/token <span class="token punctuation">\</span> <span class="token parameter variable">--header</span> <span class="token string">'Content-Type: application/x-www-form-urlencoded'</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"code=<CODE>"</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"client_id=<GOOGLE_CLIENT_ID>"</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"client_secret=<GOOGLE_CLIENT_SECRET>"</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"redirect_uri=<GOOGLE_REDIRECT_URI>"</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"grant_type=authorization_code"</span></code></pre><br><p>제대로 값이 전달되면 다음과 같은 response를 얻을 수 있다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">{</span> <span class="token string">"access_token"</span><span class="token builtin class-name">:</span> <span class="token operator"><</span>ACCESS_TOKEN<span class="token operator">></span>, <span class="token string">"expires_in"</span><span class="token builtin class-name">:</span> <span class="token number">3599</span>, <span class="token string">"refresh_token"</span><span class="token builtin class-name">:</span> <span class="token operator"><</span>REFRESH_TOKEN<span class="token operator">></span>, <span class="token string">"scope"</span><span class="token builtin class-name">:</span> <span class="token string">"https://www.googleapis.com/auth/calendar"</span>, <span class="token string">"token_type"</span><span class="token builtin class-name">:</span> <span class="token string">"Bearer"</span><span class="token punctuation">}</span></code></pre><br><h3 id="4-1-Refresh-Token을-사용해서-Access-Token을-재발급-받기"><a href="#4-1-Refresh-Token을-사용해서-Access-Token을-재발급-받기" class="headerlink" title="4-1. Refresh Token을 사용해서 Access Token을 재발급 받기"></a>4-1. Refresh Token을 사용해서 Access Token을 재발급 받기</h3><p>결과를 보면 <code>expires_in</code>이 있는데, 3599초 즉 <code>1시간</code>만 Access Token을 사용할 수 있으며 이후로는 토큰 사용이 불가능하다.</p><p>나처럼 캘린더 일정을 계속 자동화해서 잡아야 하는 경우에는 <code>refresh_token</code>을 이용해서 <code>access_token</code>을 갱신해야 한다.<br>다행이도 Google OAuth2에서 제공하는 <strong>refresh_token</strong>은 만료되지 않는다.</p><br><p>만약 <strong>refresh_token</strong> 을 사용해서 <strong>access_token</strong>을 재발급 받고 싶으면 다음의 curl을 보내주자.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">--request</span> POST <span class="token punctuation">\</span> <span class="token parameter variable">--url</span> https://oauth2.googleapis.com/token <span class="token punctuation">\</span> <span class="token parameter variable">--header</span> <span class="token string">'Content-Type: application/x-www-form-urlencoded'</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"client_id=<GOOGLE_CLIENT_ID>"</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"client_secret=<GOOGLE_CLIENT_SECRET>"</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"refresh_token=<REFRESH_TOKEN>"</span> <span class="token punctuation">\</span> <span class="token parameter variable">--data</span> <span class="token string">"grant_type=refresh_token"</span></code></pre><br><p>갱신 요청이 성공하면 새로운 <code>access_token</code>이 반환된다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">{</span> <span class="token string">"access_token"</span><span class="token builtin class-name">:</span> <span class="token operator"><</span>ACCESS_TOKEN<span class="token operator">></span>, <span class="token string">"expires_in"</span><span class="token builtin class-name">:</span> <span class="token number">3599</span>, <span class="token string">"scope"</span><span class="token builtin class-name">:</span> <span class="token string">"https://www.googleapis.com/auth/calendar"</span>, <span class="token string">"token_type"</span><span class="token builtin class-name">:</span> <span class="token string">"Bearer"</span><span class="token punctuation">}</span></code></pre><h2 id="5-JS코드로-Access-Token-Refresh-Token으로-재발급까지-을-받아보자"><a href="#5-JS코드로-Access-Token-Refresh-Token으로-재발급까지-을-받아보자" class="headerlink" title="5. JS코드로 Access Token (+Refresh Token으로 재발급까지)을 받아보자"></a>5. JS코드로 Access Token (+Refresh Token으로 재발급까지)을 받아보자</h2><p>이러한 작업들을 일일이 하기에는 여러모로 피곤하니, 코드로 한 번 구현을 해봤다.</p><p>만약 이 코드를 돌렸는데 403 Forbidden이 나온다면 GCP에서 현재 사용하는 서비스의 API 사용을 허가했는지 확인해보자.<br>서비스 리스트는 <a href="https://console.cloud.google.com/apis/api">https://console.cloud.google.com/apis/api</a> 에서 확인할 수 있다.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># .env.local</span><span class="token assign-left variable">GOOGLE_CLIENT_ID</span><span class="token operator">=</span><span class="token operator"><</span>클라이언트 ID<span class="token operator">></span><span class="token assign-left variable">GOOGLE_CLIENT_SECRET</span><span class="token operator">=</span><span class="token operator"><</span>클라이언트 보안 비밀번호<span class="token operator">></span><span class="token assign-left variable">GOOGLE_REDIRECT_URI</span><span class="token operator">=</span><span class="token operator"><</span>승인된 리디렉션 URI주소<span class="token operator">></span><span class="token assign-left variable">GOOGLE_REFRESH_TOKEN</span><span class="token operator">==</span><span class="token operator"><</span>refresh token값<span class="token operator">></span></code></pre><pre class="language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">// oauth.js</span><span class="token keyword">import</span> <span class="token punctuation">{</span> google <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'googleapis'</span><span class="token punctuation">;</span><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getOAuth2Client</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> oAuth2Client <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">google<span class="token punctuation">.</span>auth<span class="token punctuation">.</span>OAuth2</span><span class="token punctuation">(</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">GOOGLE_CLIENT_ID</span><span class="token punctuation">,</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">GOOGLE_CLIENT_SECRET</span><span class="token punctuation">,</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">GOOGLE_REDIRECT_URI</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Refresh token 설정</span> oAuth2Client<span class="token punctuation">.</span><span class="token function">setCredentials</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">refresh_token</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">GOOGLE_REFRESH_TOKEN</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// Access token 갱신</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> token <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">await</span> oAuth2Client<span class="token punctuation">.</span><span class="token function">getAccessToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> oAuth2Client<span class="token punctuation">.</span><span class="token function">setCredentials</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">access_token</span><span class="token operator">:</span> token <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> oAuth2Client<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">'Failed to refresh access token:'</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Failed to get access token.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre>]]></content>
<categories>
<category> ☁️ Cloud </category>
</categories>
<tags>
<tag> Cloud </tag>
<tag> OAuth2 </tag>
<tag> GCP </tag>
</tags>
</entry>
<entry>
<title>[NCP] 콘솔에서 NKS(Ncloud Kubernetes Service) 및 NCR(Ncloud Container Registry) 세팅으로 배포하기</title>
<link href="/2024/11/25/Cloud/NCP/Console/NKS&NCR/"/>
<url>/2024/11/25/Cloud/NCP/Console/NKS&NCR/</url>
<content type="html"><![CDATA[<p>최근 멘토링에서 ncloud를 사용하여 서버배포를 하고싶다는 요청을 받았는데, 나 또한 네이버 클라우드는 경험이 없는지라 ㅎㅎ;; 공부삼아 한 번 세팅을 해봤다.</p><p>일단 클라우드에서 Kubernetes로 container를 배포를 하기 위해서는 로직이 대략적으로 이렇게 된다.<br><img src="./2.png" width="800"></p><ul><li>설명이 어렵다 할 분들을 위해서, 전체적인 로직은 Services 쪽을 보면 이해하기 쉬울 것이다.<br><code>Container Registry</code>에 컨테이너 이미지 업로드 -> <code>Ncloud Kubernetes Service</code>의 Kubernetes에 컨테이너 이미지 배치 -> Kubernetes가 서버를 배포하도록 설정<br>이렇게만 하면 클라우드에 우리가 원하는 이미지를 띄울 수 있다.</li><li>하지만 클라우드는 <code>보안</code>이 생명인 서비스인 만큼, 모든 것들이 기본적으로 private으로 설정되며 우리는 이런 리소스에 접근하기 위해서는 여러 인증을 n번 거쳐야한다. 때문에 VPC를 설정을 예쁘게 해야한다.<br>subnet 이름은 사람마다 취향껏 맞춰 지으면 되며, 여기서는 편의상 내 마음대로 이름을 붙여봤다.<ul><li><code>k8s-server-k2</code><ul><li>클러스터로 들어오는 내부 트래픽과 외부 트래픽을 관리</li><li>컨테이너 이미지를 레지스트리에서 가져와 클러스터에 배포</li><li>클러스터가 인터넷(외부 서비스)과 연결될 수 있도록 하는 Nat 관리</li></ul></li><li><code>k8s-lb-private</code>: 클러스터 내부에서만 접근할 수 있는 서비스들을 트래픽에 전달<ul><li>특히 내부 Pod간 통신 처리</li></ul></li><li><code>k8s-lb-public</code>: 인터넷을 통해 외부 사용자들이 접근할 수 있는 서비스들에 트래픽 전달<ul><li>특히 외부에서 트래픽을 받으면 pod로 라우팅함</li></ul></li><li><code>Nat</code>: NAT Gateway의 약자로, 외부에서 Kubernetes 클러스터 내부로의 직접적인 접근을 차단하기 위해 생성</li><li><code>k8s-k2</code>: Kubernetes 클러스터를 관리함. 쉽게 Kubernetes 클러스터의 worker nodes 관리하는 곳으로 생각하면 됨</li></ul></li></ul><p>이렇게 간략하게 설명을 해봤으며, 이해가 됐다면 바로 세팅으로 들어가보자.</p><h2 id="0-NCP-콘솔-로그인"><a href="#0-NCP-콘솔-로그인" class="headerlink" title="0. NCP 콘솔 로그인"></a>0. NCP 콘솔 로그인</h2><img src="./0.png" width="800"><p>화면 맨 위 오른쪽에 <a href="https://console.ncloud.com/dashboard">콘솔</a> 버튼을 누르면 접속할 수 있다.</p><h2 id="1-VPC-생성하기"><a href="#1-VPC-생성하기" class="headerlink" title="1. VPC 생성하기"></a>1. VPC 생성하기</h2><ul><li>VPC > <a href="https://console.ncloud.com/vpc-network/vpc">VPC Management</a>에서 생성</li></ul><img src="./1.png" width="800"><h2 id="2-Subnet-생성하기"><a href="#2-Subnet-생성하기" class="headerlink" title="2. Subnet 생성하기"></a>2. Subnet 생성하기</h2><ul><li>VPC > <a href="https://console.ncloud.com/vpc-network/subnet">Subnet Management</a>에서 생성</li><li>1번에서 생성한 VPC 이름으로 설정</li></ul><img src="./3.png" width="800"><h2 id="3-NAT-Gateway-생성하기"><a href="#3-NAT-Gateway-생성하기" class="headerlink" title="3. NAT Gateway 생성하기"></a>3. NAT Gateway 생성하기</h2><ul><li>VPC > <a href="https://console.ncloud.com/vpc-network/nat-gateway">NAT Gateway</a>에서 생성</li><li>1, 2번에서 생성한 VPC 및 nat subnet 설정</li></ul><img src="./4.png" width="800"><h2 id="4-Routes-설정하기"><a href="#4-Routes-설정하기" class="headerlink" title="4. Routes 설정하기"></a>4. Routes 설정하기</h2><ul><li>VPC > Route Table > <a href="https://console.ncloud.com/vpc-network/routeTable">Route Table</a>에서 <code>private-table</code> 선택</li><li><code>Route 설정</code>에서 public access를 위해 <code>0.0.0.0/0</code>, <code>NATGW</code>, <code>nat설정의 subnet 이름</code> 추가</li></ul><img src="./5.png" width="800"><h2 id="5-Object-Storage-생성하기"><a href="#5-Object-Storage-생성하기" class="headerlink" title="5. Object Storage 생성하기"></a>5. Object Storage 생성하기</h2><ul><li>Object Storage > <a href="https://console.ncloud.com/objectStorage/objectStorageList">Bucket Management</a>에서 버킷 생성</li></ul><img src="./6.png" width="300"><h2 id="6-Container-Registry-생성하기"><a href="#6-Container-Registry-생성하기" class="headerlink" title="6. Container Registry 생성하기"></a>6. Container Registry 생성하기</h2><ul><li><a href="https://console.ncloud.com/ncr/registries">Container Registry</a>에서 생성</li><li>5번에서 생성한 버킷으로 설정</li></ul><img src="./7.png" width="800"><h2 id="7-Ncloud-Kubernetes-Service-생성하기"><a href="#7-Ncloud-Kubernetes-Service-생성하기" class="headerlink" title="7. Ncloud Kubernetes Service 생성하기"></a>7. Ncloud Kubernetes Service 생성하기</h2><h3 id="1-VPC-Ncloud-Kubernetes-Service-Clusters에서-생성"><a href="#1-VPC-Ncloud-Kubernetes-Service-Clusters에서-생성" class="headerlink" title="1) VPC Ncloud Kubernetes Service Clusters에서 생성"></a>1) <a href="https://console.ncloud.com/vnks/clusters">VPC Ncloud Kubernetes Service Clusters</a>에서 생성</h3><ul><li><p>1번에서 생성한 VPC를 선택하면 자동으로 LB Private subnet, LB Public subnet이 세팅됨</p></li><li><p>이번 실습에서는 클러스터 인증 모드를 API로 설정함<br><img src="./9.png" width="800"></p></li><li><p>설정 완료 후 다음으로 넘어가면 Nat 설정에 관한 안내창이 나오는데, 무시해도 됨(이 세팅은 3번, 5번에서 했기 때문)</p><img src="./8.png" width="400"></li></ul><h3 id="2-노드풀-설정하기"><a href="#2-노드풀-설정하기" class="headerlink" title="2) 노드풀 설정하기"></a>2) 노드풀 설정하기</h3> <img src="./10.png" width="800"><h3 id="3-인증키-설정"><a href="#3-인증키-설정" class="headerlink" title="3) 인증키 설정"></a>3) 인증키 설정</h3> <img src="./11.png" width="800"><p><br><br></p><p>모든 설정이 완료되면 아래 이미지처럼 생성됨.약 10-30분 기다려야함<br><img src="./12.png" width="800"></p><h2 id="8-사용자-인증-API-인증키-생성하기"><a href="#8-사용자-인증-API-인증키-생성하기" class="headerlink" title="8. 사용자 인증: API 인증키 생성하기"></a>8. 사용자 인증: API 인증키 생성하기</h2><ul><li>계정 관리 > <a href="https://www.ncloud.com/mypage/manage/authkey">인증키 관리</a>에서 <code>신규 API 인증키 생성</code>을 클릭하여 API 인증키 생성하기</li><li><code>Access Key ID</code> 및 <code>Secret Key</code> 값을 준비해두자. 앞으로 서비스 이용을 할 때 쓸 예정</li></ul><h2 id="9-Container-Registry에-Docker-image-업로드하기"><a href="#9-Container-Registry에-Docker-image-업로드하기" class="headerlink" title="9. Container Registry에 Docker image 업로드하기"></a>9. Container Registry에 Docker image 업로드하기</h2><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># ncloud docker login 하기</span><span class="token comment"># registry-url = container registry의 public endpoint</span>$ <span class="token function">docker</span> login <span class="token operator"><</span>registry-url<span class="token operator">></span><span class="token comment"># docker build & tag & push 하기</span><span class="token comment"># docker build: ncloud의 Kubernetes 세팅이 amd64으로 되어있어 platform 옵션 추가</span>$ <span class="token function">docker</span> build <span class="token parameter variable">--platform</span> linux/amd64 <span class="token parameter variable">-t</span> <span class="token operator"><</span>registry-url<span class="token operator">></span>/<span class="token operator"><</span>repository-name<span class="token operator">></span>:<span class="token operator"><</span>tag<span class="token operator">></span> <span class="token builtin class-name">.</span>$ <span class="token function">docker</span> tag $ <span class="token function">docker</span> push <span class="token operator"><</span>registry-url<span class="token operator">></span>/<span class="token operator"><</span>repository-name<span class="token operator">></span>:<span class="token operator"><</span>tag<span class="token operator">></span></code></pre><img src="./15.png" width="800">Push가 잘됐다면 이미지처럼 Tags탭에 들어갔을 때 push한 이미지를 확인할 수 있다.<h2 id="10-Kubernetes-설정해서-Docker-image-배포하기"><a href="#10-Kubernetes-설정해서-Docker-image-배포하기" class="headerlink" title="10. Kubernetes 설정해서 Docker image 배포하기"></a>10. Kubernetes 설정해서 Docker image 배포하기</h2><h3 id="NKS-인증키-설정"><a href="#NKS-인증키-설정" class="headerlink" title="NKS 인증키 설정"></a>NKS 인증키 설정</h3><p><a href="https://guide.ncloud-docs.com/docs/k8s-iam-auth-ncp-iam-authenticator">여기</a>를 참고하여 ncp-iam-authenticator를 설치한 후 진행</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># ncloud setup</span>$ <span class="token builtin class-name">export</span> <span class="token assign-left variable">NCLOUD_ACCESS_KEY</span><span class="token operator">=</span><span class="token operator"><</span><span class="token number">8</span>번에서-생성한-Access-Key-ID<span class="token operator">></span>$ <span class="token builtin class-name">export</span> <span class="token assign-left variable">NCLOUD_SECRET_KEY</span><span class="token operator">=</span><span class="token operator"><</span><span class="token number">8</span>번에서-생성한-Secret-Key<span class="token operator">></span>$ <span class="token builtin class-name">export</span> <span class="token assign-left variable">NCLOUD_API_GW</span><span class="token operator">=</span>https://ncloud.apigw.ntruss.com<span class="token comment"># nks 인증키 생성</span><span class="token comment"># region이 한국이면 region-code는 KR</span><span class="token comment"># 파일이 생성되지 않거나, 빈 파일이 생성됐다면 문제가 있는 것</span>$ ncp-iam-authenticator create-kubeconfig <span class="token parameter variable">--region</span> <span class="token operator"><</span>region-code<span class="token operator">></span> <span class="token parameter variable">--clusterUuid</span> <span class="token operator"><</span>NKS에서 생성한 cluster의 uuid<span class="token operator">></span> <span class="token parameter variable">--output</span> kubeconfig.yaml$ <span class="token builtin class-name">export</span> <span class="token assign-left variable">KUBECONFIG</span><span class="token operator">=</span><span class="token string">"<kubeconfig.yaml의 파일 절대경로>"</span><span class="token comment"># Kubernetes가 제대로 설정됐는지 확인</span>$ kubectl get ns <span class="token comment"># namespace 조회. 아래같이 나오면 성공</span>NAME STATUS AGEdefault Active 40hkube-node-lease Active 40hkube-public Active 40hkube-system Active 40h</code></pre><h3 id="Kubernetes를-위한-yaml파일-생성"><a href="#Kubernetes를-위한-yaml파일-생성" class="headerlink" title="Kubernetes를 위한 yaml파일 생성"></a>Kubernetes를 위한 yaml파일 생성</h3><pre class="language-yaml" data-language="yaml"><code class="language-yaml"><span class="token comment"># deployment.yaml</span><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> apps/v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> Deployment<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app<span class="token punctuation">-</span>deployment <span class="token key atrule">namespace</span><span class="token punctuation">:</span> default<span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">matchLabels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">template</span><span class="token punctuation">:</span> <span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">containers</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">image</span><span class="token punctuation">:</span> <registry<span class="token punctuation">-</span>url<span class="token punctuation">></span>/<repository<span class="token punctuation">-</span>name<span class="token punctuation">></span><span class="token punctuation">:</span><tag<span class="token punctuation">></span> <span class="token key atrule">imagePullPolicy</span><span class="token punctuation">:</span> Always <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">containerPort</span><span class="token punctuation">:</span> <span class="token number">8000</span> <span class="token key atrule">imagePullSecrets</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> regcred</code></pre><pre class="language-yaml" data-language="yaml"><code class="language-yaml"><span class="token comment"># service.yaml</span><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> Service<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app<span class="token punctuation">-</span>service <span class="token key atrule">annotations</span><span class="token punctuation">:</span> <span class="token key atrule">service.beta.Kubernetes.io/ncloud-load-balancer-layer-type</span><span class="token punctuation">:</span> <span class="token string">"nplb"</span> <span class="token key atrule">service.beta.Kubernetes.io/ncloud-load-balancer-size</span><span class="token punctuation">:</span> <span class="token string">"SMALL"</span><span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">type</span><span class="token punctuation">:</span> LoadBalancer <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">80</span> <span class="token key atrule">targetPort</span><span class="token punctuation">:</span> <span class="token number">8000</span></code></pre><h3 id="Kubernetes에-템플릿-배포-및-서비스-확인"><a href="#Kubernetes에-템플릿-배포-및-서비스-확인" class="headerlink" title="Kubernetes에 템플릿 배포 및 서비스 확인"></a>Kubernetes에 템플릿 배포 및 서비스 확인</h3><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># kubenetes secret object 생성</span>$ kubectl create secret docker-registry regcred <span class="token punctuation">\</span> --docker-server<span class="token operator">=</span><span class="token operator"><</span>registry-url<span class="token operator">></span> <span class="token punctuation">\</span> --docker-username<span class="token operator">=</span><span class="token operator"><</span><span class="token number">8</span>번에서-생성한-Access-Key-ID<span class="token operator">></span> <span class="token punctuation">\</span> --docker-password<span class="token operator">=</span><span class="token operator"><</span><span class="token number">8</span>번에서-생성한-Secret-Key<span class="token operator">></span> <span class="token punctuation">\</span> --docker-email<span class="token operator">=</span><span class="token operator"><</span>ncloud-email-주소<span class="token operator">></span>$ kubectl get secrets <span class="token comment"># 생성이 잘 됐는지 확인하는 명령어</span><span class="token comment"># yaml 템플릿 업로드</span>$ kubectl apply <span class="token parameter variable">-f</span> deployment.yaml$ kubectl apply <span class="token parameter variable">-f</span> service.yaml$ kubectl get pods <span class="token comment"># status running인지 확인하는 명령어</span><span class="token comment"># 클라우드에 배포된 IP주소 확인하기</span><span class="token comment"># LoadBalancer의 External IP 주소 = 곧 클라우드에 배포된 서비스 IP 주소</span>$ kubectl get servicesNAME TYPE CLUSTER-IP EXTERNAL-IP PORT<span class="token punctuation">(</span>S<span class="token punctuation">)</span> AGEKubernetes ClusterIP <span class="token number">198.19</span>.128.1 <span class="token operator"><</span>none<span class="token operator">></span> <span class="token number">443</span>/TCP 40hmy-app-service LoadBalancer <span class="token number">198.19</span>.255.184 default-my-app-service-cfe50-100590090-dcc6483894d3.kr.lb.naverncp.com <span class="token number">80</span>:30167/TCP 39h</code></pre><h2 id="11-배포-페이지-접근해보기"><a href="#11-배포-페이지-접근해보기" class="headerlink" title="11. 배포 페이지 접근해보기"></a>11. 배포 페이지 접근해보기</h2><p>글 업로드 시점 기준으로 서비스 중단해서 접속 안됩니다. 참고하세용<br><img src="./16.png" width="800"></p><h2 id="12-만약-Docker-이미지를-업데이트를-해서-서버에-반영하고-싶다면"><a href="#12-만약-Docker-이미지를-업데이트를-해서-서버에-반영하고-싶다면" class="headerlink" title="12. 만약 Docker 이미지를 업데이트를 해서 서버에 반영하고 싶다면?"></a>12. 만약 Docker 이미지를 업데이트를 해서 서버에 반영하고 싶다면?</h2><p>Kubernetes의 deployment.yaml에 이미 <code>imagePullPolicy</code> 세팅이 <code>Always</code>로 되어있어서 같은 태그를 push했을 때는 자동으로 Kubernetes가 이미지를 추적하여 업데이트를 할 것이다.<br>다만 사람마다 태그 규칙이 다르며, 이 때문에 tag 버전이 업데이트 된 이미지를 업로드를 한다면 Kubernetes가 알아차리지 못하니 우리가 알려줘야 한다.</p><p>다음은 kubernets에게 새로운 container image가 생겼다는 것을 알려주는 작업이다.</p><ol><li><code>deployment.yaml</code> 수정</li></ol><pre class="language-yaml" data-language="yaml"><code class="language-yaml"><span class="token comment"># deployment.yaml</span><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> apps/v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> Deployment<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app<span class="token punctuation">-</span>deployment <span class="token key atrule">namespace</span><span class="token punctuation">:</span> default<span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token punctuation">...</span> <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">containers</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">image</span><span class="token punctuation">:</span> <registry<span class="token punctuation">-</span>url<span class="token punctuation">></span>/<repository<span class="token punctuation">-</span>name<span class="token punctuation">></span><span class="token punctuation">:</span><tag<span class="token punctuation">></span> <span class="token comment"># tag에 맞춰 수정</span><span class="token punctuation">...</span></code></pre><ol start="2"><li>deployment 서비스 재시작</li></ol><pre class="language-bash" data-language="bash"><code class="language-bash">kubectl rollout restart deployment <span class="token operator"><</span>deployment.yaml의 metadata.name<span class="token operator">></span></code></pre>]]></content>
<categories>
<category> ☁️ Cloud </category>
</categories>
<tags>
<tag> Cloud </tag>
<tag> Container </tag>
<tag> Docker </tag>
<tag> Network </tag>
<tag> NCP </tag>
<tag> VPC </tag>
<tag> Kubernetes </tag>
</tags>
</entry>
<entry>
<title>[TIPS] Mermaid 명령어로 Diagram 그리기</title>
<link href="/2024/11/23/Tips/Tool/"/>
<url>/2024/11/23/Tips/Tool/</url>
<content type="html"><![CDATA[<p>프로젝트를 하다보면 flow chart나 diagram을 그려야 하는데, 이게 여간 귀찮은 일이 아닐 수 없다.<br>빠르고 간단하게 그리고 싶은 분들은 Mermaid 명령어로 그려보는걸 추천한다.</p><h2 id="Mermaid"><a href="#Mermaid" class="headerlink" title="Mermaid"></a><a href="https://mermaid.js.org/">Mermaid</a></h2><img src="./3.png" width="150"><p>Markdown(마크다운) 명령어로 diagram을 그릴 수 있는 툴이다.<br>압도적인 점유율을 자랑하는 우리의 Github에서는 이 문법을 지원하기 때문에, README.md파일 같은 곳에서 작성해두면 내용을 시각하하여 볼 수 있는 장점이 있다.</p><h2 id="Mermaid-ChatGPT로-간단하게-만들기"><a href="#Mermaid-ChatGPT로-간단하게-만들기" class="headerlink" title="Mermaid + ChatGPT로 간단하게 만들기"></a>Mermaid + ChatGPT로 간단하게 만들기</h2><p>사용법을 공부해서 적용하면 되지만 아무래도 어떤 관계인지 정리를 해야하니 머리가 여간 복잡할 수 밖에 없다…<br>따라서 21세기 최고의 도구인 AI를 이용해보도록 하자.</p><ol><li><a href="https://chatgpt.com/">ChatGPT</a>에 어떤 그림을 그릴지 설명한다. 이 때 mermaid 문법으로 써달라고 요청하면 예쁘게 뽑아준다. ctrl+c를 해준다.</li></ol><img src="./1.png" width="600"><ol start="2"><li><a href="https://mermaid.live/edit">Mermaid Live Editor</a>에 접속해서 ctrl+v를 해준다.</li></ol><img src="./2.png" width="600">]]></content>
<categories>
<category> 💾 Etc </category>
</categories>
<tags>
<tag> Diagram </tag>
</tags>
</entry>
<entry>
<title>Docker 정리</title>
<link href="/2024/11/11/DevOps/Docker/"/>
<url>/2024/11/11/DevOps/Docker/</url>
<content type="html"><![CDATA[<p>이 글을 읽는 사람은 먼저 <a href="/2024/07/12/DevOps/Container/" title="Container 정리">Container 정리</a>부터 정리하고 읽는 것을 추천한다.</p><p>Docker는 컨테이너 기반의 애플리케이션 개발, 배포, 실행을 자동화하는 오픈소스로 대부분의 기업들에서 컨테이너 관련 기술로 채택하는 기술이다.</p><h2 id="1-Docker-개념"><a href="#1-Docker-개념" class="headerlink" title="1. Docker 개념"></a>1. Docker 개념</h2><h3 id="Docker-Image"><a href="#Docker-Image" class="headerlink" title="Docker Image"></a>Docker Image</h3><ul><li>컨테이너를 실행하기 위한 읽기 전용 템플릿</li><li>애플리케이션과 실행 환경 (라이브러리, 의존성) 포함</li><li>이미지 빌드를 위해서는 <code>Dockerfile</code> 스크립트 파일이 필요: 단계별로 애플리케이션 설치 및 환경 설정 정의</li></ul><h3 id="Docker-Container"><a href="#Docker-Container" class="headerlink" title="Docker Container"></a>Docker Container</h3><ul><li>이미지에서 생성된 실행 가능한 인스턴스</li><li>애플리케이션과 환경이 실행되는 독립적인 단위</li></ul><h3 id="Docker-Registry"><a href="#Docker-Registry" class="headerlink" title="Docker Registry"></a>Docker Registry</h3><ul><li>이미지 저장소 (ex. Docker Hub, AWS ECR, GCP Container Registry 등 포함)</li></ul><h2 id="2-Docker-명령어-정리"><a href="#2-Docker-명령어-정리" class="headerlink" title="2. Docker 명령어 정리"></a>2. Docker 명령어 정리</h2><h3 id="1-Docker-설치-확인"><a href="#1-Docker-설치-확인" class="headerlink" title="1) Docker 설치 확인"></a>1) Docker 설치 확인</h3><ul><li>version 확인: <code>docker --version</code><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> <span class="token parameter variable">--version</span>Docker version <span class="token number">20.10</span>.23, build <span class="token number">7155243</span></code></pre></li></ul><br><ul><li>info 확인: <code>docker info</code><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> infoClient: Context: default Debug Mode: <span class="token boolean">false</span> Plugins: buildx: Docker Buildx <span class="token punctuation">(</span>Docker Inc., v0.10.3<span class="token punctuation">)</span> compose: Docker Compose <span class="token punctuation">(</span>Docker Inc., v2.15.1<span class="token punctuation">)</span> dev: Docker Dev Environments <span class="token punctuation">(</span>Docker Inc., v0.1.0<span class="token punctuation">)</span> extension: Manages Docker extensions <span class="token punctuation">(</span>Docker Inc., v0.2.18<span class="token punctuation">)</span> sbom: View the packaged-based Software Bill Of Materials <span class="token punctuation">(</span>SBOM<span class="token punctuation">)</span> <span class="token keyword">for</span> an image <span class="token punctuation">(</span>Anchore Inc., <span class="token number">0.6</span>.0<span class="token punctuation">)</span> scan: Docker Scan <span class="token punctuation">(</span>Docker Inc., v0.25.0<span class="token punctuation">)</span> scout: Command line tool <span class="token keyword">for</span> Docker Scout <span class="token punctuation">(</span>Docker Inc., v0.6.0<span class="token punctuation">)</span>Server: Containers: <span class="token number">4</span> Running: <span class="token number">0</span> Paused: <span class="token number">0</span> Stopped: <span class="token number">4</span> Images: <span class="token number">14</span> Server Version: <span class="token number">20.10</span>.23 Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: <span class="token boolean">true</span> Native Overlay Diff: <span class="token boolean">true</span> userxattr: <span class="token boolean">false</span> Logging Driver: json-file Cgroup Driver: cgroupfs Cgroup Version: <span class="token number">2</span> Plugins: Volume: <span class="token builtin class-name">local</span> Network: bridge <span class="token function">host</span> ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file <span class="token builtin class-name">local</span> logentries splunk syslog Swarm: inactive Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc Default Runtime: runc Init Binary: docker-init containerd version: 2456e983eb9e37e47538f59ea18f2043c9a73640 runc version: v1.1.4-0-g5fd4c4d init version: de40ad0 Security Options: seccomp Profile: default cgroupns Kernel Version: <span class="token number">5.15</span>.49-linuxkit Operating System: Docker Desktop OSType: linux Architecture: aarch64 CPUs: <span class="token number">4</span> Total Memory: <span class="token number">7</span>.668GiB Name: docker-desktop ID: 276G:OJLG:WHVN:JV37:CX4G:VAC2:K2AY:TJPC:M2RA:D5NR:DWTP:HU2G Docker Root Dir: /var/lib/docker Debug Mode: <span class="token boolean">false</span> HTTP Proxy: http.docker.internal:3128 HTTPS Proxy: http.docker.internal:3128 No Proxy: hubproxy.docker.internal Registry: https://index.docker.io/v1/ Labels: Experimental: <span class="token boolean">false</span> Insecure Registries: hubproxy.docker.internal:5000 <span class="token number">127.0</span>.0.0/8 Live Restore Enabled: <span class="token boolean">false</span></code></pre></li></ul><h3 id="2-Docker-이미지-관리"><a href="#2-Docker-이미지-관리" class="headerlink" title="2) Docker 이미지 관리"></a>2) Docker 이미지 관리</h3><ul><li>Docker Hub에서 이미지 검색: <code>docker search <이미지명></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> search <span class="token builtin class-name">test</span>NAME DESCRIPTION STARS OFFICIAL AUTOMATEDimmcantation/test Immcantation unit <span class="token builtin class-name">test</span> image <span class="token number">1</span>pachyderm/test <span class="token number">0</span>islandora/test This image is exclusively used <span class="token keyword">for</span> manually … <span class="token number">0</span>okteto/test <span class="token number">0</span>amir20/test <span class="token number">0</span>kubeovn/test <span class="token number">0</span>voxpupuli/test Container repo to <span class="token builtin class-name">test</span> container build and V… <span class="token number">0</span>corpusops/test <span class="token number">0</span>ratelimitpreview/test <span class="token number">0</span>test/testimage20130921141042 <span class="token number">0</span>test/testimage20130919235830 <span class="token number">0</span>test/testimage20130921221004 <span class="token number">0</span>test/testimage20130919223925 <span class="token number">0</span>test/testimage20130921190956 <span class="token number">0</span>test/testimage20130922032014 <span class="token number">0</span>doct15/test <span class="token number">0</span>flypenguin/test A <span class="token builtin class-name">test</span> container <span class="token keyword">for</span> <span class="token punctuation">..</span>. k8s <span class="token builtin class-name">:</span><span class="token punctuation">)</span> <span class="token number">0</span> <span class="token punctuation">[</span>OK<span class="token punctuation">]</span>brimworks/test <span class="token number">0</span>flvranckx/test <span class="token builtin class-name">test</span> <span class="token number">0</span>chiphwang/test <span class="token number">0</span>divyag2411/test <span class="token number">0</span>realmaccess/test <span class="token number">0</span>devopsatburst/test <span class="token number">0</span>hopar/test <span class="token builtin class-name">test</span> descr <span class="token number">0</span>femycatherine/test <span class="token builtin class-name">test</span> <span class="token number">0</span></code></pre><br><ul><li>특정 이미지를 로컬로 다운로드: <code>docker pull <이미지명>:<태그></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> pull nginx:latestlatest: Pulling from library/nginxDigest: sha256:bc5eac5eafc581aeda3008b4b1f07ebba230de2f27d47767129a6a905c84f470Status: Image is up to <span class="token function">date</span> <span class="token keyword">for</span> nginx:latestdocker.io/library/nginx:latest</code></pre><br><ul><li>로컬에 저장된 이미지 나열: <code>docker images</code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> imagesREPOSITORY TAG IMAGE ID CREATED SIZEdjango-app latest cbb6dff7c55a <span class="token number">2</span> days ago <span class="token number">2</span>.12GBpostgres <span class="token number">15</span>-alpine c558f49f952d <span class="token number">3</span> days ago 256MBpython <span class="token number">3.12</span>-slim e359a8be29f6 <span class="token number">5</span> weeks ago 150MBredis alpine 4a6fa001a1b0 <span class="token number">7</span> weeks ago <span class="token number">48</span>.3MBnginx latest 7a3f95c07812 <span class="token number">7</span> weeks ago 197MB</code></pre><br><ul><li>로컬에 저장된 이미지 삭제: `docker rmi <이미지ID></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> imagesREPOSITORY TAG IMAGE ID CREATED SIZEpython latest 62639b6c3f3e <span class="token number">5</span> weeks ago <span class="token number">1</span>.02GB$ <span class="token function">docker</span> rmi <span class="token number">626</span> <span class="token comment"># 전부 다 안써도 특정 이미지인지 인지할 수 있으면 됨</span>Untagged: python:latestUntagged: python@sha256:bc78d3c007f86dbb87d711b8b082d9d564b8025487e780d24ccb8581d83ef8b0Deleted: sha256:62639b6c3f3e77ebbbf3f18802d08351636f8dd6e0a94d1b225e9f5dc1c88044Deleted: sha256:476e07a0f38bafed4e474712d0e3d37dbcb7ff90d06e17bb8d1fc4b2f14e7d35Deleted: sha256:2c190c9407be9104a7d2b741a4d91364bf2be04b81784dddaf13c9f188562e4dDeleted: sha256:e00fefe3633c24a34f09149c5f3dbc2330630a365ffedff052f988c3585a1ee2Deleted: sha256:c6d7f5d3800ca99cabcb02e8b21872f3a09cebe3973f5c98ebb3840435e0fd0fDeleted: sha256:d4a8223e1c6c5461929b8b25b9bdb78d194803d9ca62f94f43df43d8e4b17e47Deleted: sha256:a354dc33fe33b2600cf4fb75dd3ad14d4e0eecb77da53b75d7aefa7487e3bd17Deleted: sha256:ec8ae7dad7aba50e0f8bff1dc969d34d3584fb7ada6ce9948dad83e95939b5cc</code></pre><h3 id="3-Docker-컨테이너-관리"><a href="#3-Docker-컨테이너-관리" class="headerlink" title="3) Docker 컨테이너 관리"></a>3) Docker 컨테이너 관리</h3><ul><li>컨테이너 실행: <code>docker run <컨테이너명> -p <호스트포트>:<컨테이너포트> <이미지명> <option: 컨테이너 내부 명령어></code><ul><li><code>-d</code>: 백그라운드 실행</li><li><code>--name <컨테이너명></code>: 컨테이너 이름 설정</li><li><code>-p <호스트포트>:<컨테이너포트></code>: 포트 매핑</li><li><code>-e <변수=값></code>: 환경변수</li></ul></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> imagesREPOSITORY TAG IMAGE ID CREATED SIZEpython latest 62639b6c3f3e <span class="token number">5</span> weeks ago <span class="token number">1</span>.02GBpython <span class="token number">3.12</span>-slim e359a8be29f6 <span class="token number">5</span> weeks ago 150MB<span class="token comment"># my-python이라는 컨테이너명으로</span><span class="token comment"># 컨테이너 포트가 56인걸 호스트포트 1234로 연결</span><span class="token comment"># 이미지명은 python이며, 백그라운드에서 실행</span>$ <span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">--name</span> my-python <span class="token parameter variable">-p</span> <span class="token number">1234</span>:56 pythonb7711eb537add805b8628277d4c677dd3d91e009d3b64bbac81e60fa84f53928<span class="token comment"># 환경변수가 Foo를 설정하고 값을 bar로 설정</span><span class="token comment"># 컨테이너 실행 이미지는 python:3.12-slim</span><span class="token comment"># 컨테이너 내부 실행 명령어는 env (컨테이너 내부의 모든 환경변수 출력하는 unix 명령어)</span>$ <span class="token function">docker</span> run <span class="token parameter variable">-e</span> <span class="token assign-left variable">FOO</span><span class="token operator">=</span>bar python:3.12-slim <span class="token function">env</span><span class="token assign-left variable"><span class="token environment constant">PATH</span></span><span class="token operator">=</span>/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin<span class="token assign-left variable"><span class="token environment constant">HOSTNAME</span></span><span class="token operator">=</span>b20562c470be<span class="token assign-left variable">FOO</span><span class="token operator">=</span>bar<span class="token assign-left variable"><span class="token environment constant">LANG</span></span><span class="token operator">=</span>C.UTF-8<span class="token assign-left variable">GPG_KEY</span><span class="token operator">=</span>7169605F62C751356D054A26A821E680E5FA6305<span class="token assign-left variable">PYTHON_VERSION</span><span class="token operator">=</span><span class="token number">3.12</span>.7<span class="token assign-left variable">PYTHON_SHA256</span><span class="token operator">=</span>24887b92e2afd4a2ac602419ad4b596372f67ac9b077190f459aba390faf5550<span class="token assign-left variable"><span class="token environment constant">HOME</span></span><span class="token operator">=</span>/root</code></pre><br><ul><li>실행 중인 컨테이너 확인: <code>docker ps</code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> imagesREPOSITORY TAG IMAGE ID CREATED SIZEdjango-app latest cbb6dff7c55a <span class="token number">2</span> days ago <span class="token number">2</span>.12GB$ <span class="token function">docker</span> run <span class="token parameter variable">-d</span> django-app:latest275bdef89cc40711dad78833d7b5aeb27f75b4e423bea1279564a99dece45178$ <span class="token function">docker</span> <span class="token function">ps</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES275bdef89cc4 django-app:latest <span class="token string">"/bin/bash -c 'sourc…"</span> <span class="token number">3</span> seconds ago Up <span class="token number">2</span> seconds <span class="token number">8000</span>/tcp relaxed_euclid</code></pre><br><ul><li>모든 컨테이너 확인 (중단된 컨테이너 포함): <code>docker ps -a</code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> <span class="token function">ps</span> <span class="token parameter variable">-a</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES275bdef89cc4 django-app:latest <span class="token string">"/bin/bash -c 'sourc…"</span> About a minute ago Up About a minute <span class="token number">8000</span>/tcp relaxed_euclidf6d665ae418b python:3.12-slim <span class="token string">"python3"</span> <span class="token number">2</span> minutes ago Exited <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token number">2</span> minutes ago intelligent_easley7c72d2379677 python:3.12-slim <span class="token string">"-d"</span> <span class="token number">2</span> minutes ago</code></pre><ul><li>컨테이너 내부 접속: `docker exec -it <컨테이너ID> /bin/bash</li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> <span class="token function">ps</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES275bdef89cc4 django-app:latest <span class="token string">"/bin/bash -c 'sourc…"</span> <span class="token number">7</span> minutes ago Up <span class="token number">7</span> minutes <span class="token number">8000</span>/tcp relaxed_euclid$ <span class="token function">docker</span> <span class="token builtin class-name">exec</span> <span class="token parameter variable">-it</span> 275bdef89cc4 /bin/bash$ root@275bdef89cc4:/app<span class="token comment">#</span></code></pre><br><ul><li>실행 중인 컨테이너 중지: <code>docker stop <컨테이너ID></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> <span class="token function">ps</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES275bdef89cc4 django-app:latest <span class="token string">"/bin/bash -c 'sourc…"</span> <span class="token number">8</span> minutes ago Up <span class="token number">8</span> minutes <span class="token number">8000</span>/tcp relaxed_euclid$ <span class="token function">docker</span> stop 275bdef89cc4275bdef89cc4$ <span class="token function">docker</span> <span class="token function">ps</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES$</code></pre><br><ul><li>중단된 컨테이너 시작: <code>docker start <컨테이너ID></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> <span class="token function">ps</span> <span class="token comment"># 실행 중인 컨테이너 없음</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES$ <span class="token function">docker</span> <span class="token function">ps</span> <span class="token parameter variable">-a</span> <span class="token comment"># 중단된 컨테이너를 확인해봄</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES275bdef89cc4 django-app:latest <span class="token string">"/bin/bash -c 'sourc…"</span> <span class="token number">10</span> minutes ago Exited <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token number">2</span> minutes ago relaxed_euclid$ <span class="token function">docker</span> start 275bdef89cc4 <span class="token comment"># 최근에 중단된거 재시작</span>275bdef89cc4$ <span class="token variable">$docker</span> <span class="token function">ps</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES275bdef89cc4 django-app:latest <span class="token string">"/bin/bash -c 'sourc…"</span> <span class="token number">11</span> minutes ago Up <span class="token number">3</span> seconds <span class="token number">8000</span>/tcp relaxed_euclid</code></pre><br><ul><li>중단된 컨테이너 삭제: <code>docker rm <컨테이너ID></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> <span class="token function">ps</span> <span class="token parameter variable">-a</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES275bdef89cc4 django-app:latest <span class="token string">"/bin/bash -c 'sourc…"</span> <span class="token number">15</span> minutes ago Exited <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token number">4</span> minutes ago relaxed_euclidf6d665ae418b python:3.12-slim <span class="token string">"python3"</span> <span class="token number">17</span> minutes ago Exited <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token number">17</span> minutes ago intelligent_easley7c72d2379677 python:3.12-slim <span class="token string">"-d"</span> <span class="token number">17</span> minutes ago Created adoring_jang$ <span class="token function">docker</span> <span class="token function">rm</span> <span class="token number">27</span> <span class="token comment"># ID를 전부 다 쓰지 않아도, 해당 이미지를 찾을 수 있을 만큼만 작성해도 알아서 찾아서 종료함. 여러개 연속으로 쓰면 모두 타겟 잡아서 종료</span><span class="token number">27</span>$ <span class="token function">docker</span> <span class="token function">ps</span> <span class="token parameter variable">-a</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESf6d665ae418b python:3.12-slim <span class="token string">"python3"</span> <span class="token number">17</span> minutes ago Exited <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token number">17</span> minutes ago intelligent_easley7c72d2379677 python:3.12-slim <span class="token string">"-d"</span> <span class="token number">17</span> minutes ago Created adoring_jang</code></pre><h3 id="4-Docker-컨테이너-로그-확인"><a href="#4-Docker-컨테이너-로그-확인" class="headerlink" title="4) Docker 컨테이너 로그 확인"></a>4) Docker 컨테이너 로그 확인</h3><ul><li>컨테이너 실행 로그 출력: <code>docker logs <컨테이너ID></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> imagesREPOSITORY TAG IMAGE ID CREATED SIZEdjango-app latest cbb6dff7c55a <span class="token number">2</span> days ago <span class="token number">2</span>.12GB$ <span class="token function">docker</span> run <span class="token parameter variable">-d</span> django-appbdb4eb9c03161d8427278f3cfa5e96505d52d0aa76064ad0145ee88506cdbd5b$ <span class="token function">docker</span> <span class="token function">ps</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESbdb4eb9c0316 django-app <span class="token string">"/bin/bash -c 'sourc…"</span> <span class="token number">5</span> seconds ago Up <span class="token number">4</span> seconds <span class="token number">8000</span>/tcp awesome_mccarthy$ <span class="token function">docker</span> logs bdb4Applying database migrations<span class="token punctuation">..</span>.Collecting static files<span class="token punctuation">..</span>.<span class="token number">168</span> static files copied to <span class="token string">'/app/src/static'</span><span class="token builtin class-name">.</span>Starting Gunicorn<span class="token punctuation">..</span>.<span class="token punctuation">[</span><span class="token number">2024</span>-11-11 <span class="token number">17</span>:32:11 +0000<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>INFO<span class="token punctuation">]</span> Starting gunicorn <span class="token number">23.0</span>.0<span class="token punctuation">[</span><span class="token number">2024</span>-11-11 <span class="token number">17</span>:32:11 +0000<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>INFO<span class="token punctuation">]</span> Listening at: http://0.0.0.0:8000 <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2024</span>-11-11 <span class="token number">17</span>:32:11 +0000<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>INFO<span class="token punctuation">]</span> Using worker: <span class="token function">sync</span><span class="token punctuation">[</span><span class="token number">2024</span>-11-11 <span class="token number">17</span>:32:11 +0000<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">609</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>INFO<span class="token punctuation">]</span> Booting worker with pid: <span class="token number">609</span></code></pre><h3 id="5-Docker-네트워크-관리"><a href="#5-Docker-네트워크-관리" class="headerlink" title="5) Docker 네트워크 관리"></a>5) Docker 네트워크 관리</h3><ul><li>네트워크 생성: <code>docker network create <네트워크명></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> network create mynwbeb1bf647de5e6a2ea2debcd45a095b2c94b217a0c645e05ff33fd122ffdfe8a</code></pre><br><ul><li>컨테이너를 특정 네트워크에 연결: <code>docker run --name <컨테이너명> --network <네트워크명> <이미지명></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> run <span class="token parameter variable">-d</span> django-appb4aa6fefecb97198ea2f7824f43e7eeb62ec3f065722b84a2d34ff9e1f834708$ <span class="token function">docker</span> run <span class="token parameter variable">--name</span> b4aa <span class="token parameter variable">--network</span> mynwApplying database migrations<span class="token punctuation">..</span>.Collecting static files<span class="token punctuation">..</span>.<span class="token number">168</span> static files copied to <span class="token string">'/app/src/static'</span><span class="token builtin class-name">.</span>Starting Gunicorn<span class="token punctuation">..</span>.<span class="token punctuation">[</span><span class="token number">2024</span>-11-11 <span class="token number">17</span>:32:11 +0000<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>INFO<span class="token punctuation">]</span> Starting gunicorn <span class="token number">23.0</span>.0<span class="token punctuation">[</span><span class="token number">2024</span>-11-11 <span class="token number">17</span>:32:11 +0000<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>INFO<span class="token punctuation">]</span> Listening at: http://0.0.0.0:8000 <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2024</span>-11-11 <span class="token number">17</span>:32:11 +0000<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>INFO<span class="token punctuation">]</span> Using worker: <span class="token function">sync</span><span class="token punctuation">[</span><span class="token number">2024</span>-11-11 <span class="token number">17</span>:32:11 +0000<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token number">609</span><span class="token punctuation">]</span> <span class="token punctuation">[</span>INFO<span class="token punctuation">]</span> Booting worker with pid: <span class="token number">609</span></code></pre><br><ul><li>Docker 네트워크 리스트 확인: <code>docker network ls</code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> network <span class="token function">ls</span>NETWORK ID NAME DRIVER SCOPE63d6218decce bridge bridge <span class="token builtin class-name">local</span>8207a8266c78 <span class="token function">host</span> <span class="token function">host</span> <span class="token builtin class-name">local</span>beb1bf647de5 mynw bridge <span class="token builtin class-name">local</span>5db98f6636e1 none null <span class="token builtin class-name">local</span></code></pre><br><ul><li>사용자 정의 네트워크 삭제: <code>docker network rm <네트워크명></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> network <span class="token function">ls</span>NETWORK ID NAME DRIVER SCOPE63d6218decce bridge bridge <span class="token builtin class-name">local</span>8207a8266c78 <span class="token function">host</span> <span class="token function">host</span> <span class="token builtin class-name">local</span>beb1bf647de5 mynw bridge <span class="token builtin class-name">local</span>5db98f6636e1 none null <span class="token builtin class-name">local</span>$ <span class="token function">docker</span> network <span class="token function">rm</span> mynwmynw$ <span class="token function">docker</span> network <span class="token function">ls</span>NETWORK ID NAME DRIVER SCOPE63d6218decce bridge bridge <span class="token builtin class-name">local</span>8207a8266c78 <span class="token function">host</span> <span class="token function">host</span> <span class="token builtin class-name">local</span>5db98f6636e1 none null <span class="token builtin class-name">local</span>$ <span class="token function">docker</span> network <span class="token function">rm</span> bridge <span class="token comment"># 사전 정의된 것에 대해서는 오류 발생</span>Error response from daemon: bridge is a pre-defined network and cannot be removed</code></pre><h3 id="6-Docker-Volume-관리"><a href="#6-Docker-Volume-관리" class="headerlink" title="6) Docker Volume 관리"></a>6) Docker Volume 관리</h3><ul><li>데이터를 저장할 볼륨 생성: <code>docker volume create <볼륨명></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> volume create myvomyvo</code></pre><br><ul><li>볼륨을 컨테이너에 마운트: <code>docker run -d --name <컨테이너명> -v <볼륨명>:<컨테이너경로> <이미지명></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> imagesREPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 7a3f95c07812 <span class="token number">7</span> weeks ago 197MB$ <span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">--name</span> my-nginx <span class="token parameter variable">-v</span> myvo:/app/data nginx007310e795a8096434dac9a75b83e238c0a698e36f660fe0e4552cf4b2d34df5</code></pre><br><ul><li>생성된 볼륨 리스트 확인: <code>docker volume ls</code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> volume <span class="token function">ls</span><span class="token function">docker</span> volume <span class="token function">ls</span>DRIVER VOLUME NAME<span class="token builtin class-name">local</span> back_media_volume<span class="token builtin class-name">local</span> back_postgres_data<span class="token builtin class-name">local</span> back_static_volume<span class="token builtin class-name">local</span> myvo</code></pre><br><ul><li>특정 볼륨 삭제: <code>docker volume rm <볼륨명></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> volume <span class="token function">ls</span>DRIVER VOLUME NAME<span class="token builtin class-name">local</span> back_media_volume<span class="token builtin class-name">local</span> back_postgres_data<span class="token builtin class-name">local</span> back_static_volume<span class="token builtin class-name">local</span> myvo$ <span class="token function">docker</span> volume <span class="token function">rm</span> back_postgres_databack_postgres_data<span class="token comment"># 이미 볼륨이 컨테이너에 마운트 되어있으면 삭제 불가능</span>$ <span class="token function">docker</span> <span class="token function">ps</span> <span class="token parameter variable">-a</span>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES007310e795a8 nginx <span class="token string">"/docker-entrypoint.…"</span> <span class="token number">4</span> minutes ago Up <span class="token number">4</span> minutes <span class="token number">80</span>/tcp my-nginx <span class="token comment"># 여기에 myvo가 마운트 되어있음</span>$ <span class="token function">docker</span> volume <span class="token function">rm</span> myvoError response from daemon: remove myvo: volume is <span class="token keyword">in</span> use - <span class="token punctuation">[</span>007310e795a8096434dac9a75b83e238c0a698e36f660fe0e4552cf4b2d34df5<span class="token punctuation">]</span></code></pre><h3 id="7-Dockerfile로-이미지-빌드"><a href="#7-Dockerfile로-이미지-빌드" class="headerlink" title="7) Dockerfile로 이미지 빌드"></a>7) Dockerfile로 이미지 빌드</h3><ul><li><code>docker build -t <이미지명>:<태그> <Dockerfile 경로></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> build <span class="token parameter variable">-t</span> my-app:1.0 <span class="token builtin class-name">.</span><span class="token punctuation">[</span>+<span class="token punctuation">]</span> Building <span class="token number">0</span>.0s <span class="token punctuation">(</span><span class="token number">2</span>/2<span class="token punctuation">)</span> FINISHED <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span>internal<span class="token punctuation">]</span> load build definition from Dockerfile <span class="token number">0</span>.0s <span class="token operator">=</span><span class="token operator">></span> <span class="token operator">=</span><span class="token operator">></span> transferring dockerfile: 2B <span class="token number">0</span>.0s <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span>internal<span class="token punctuation">]</span> load <span class="token builtin class-name">.</span> dockerignore <span class="token number">0</span>.0s <span class="token operator">=</span><span class="token operator">></span> <span class="token operator">=</span><span class="token operator">></span> transferring context: 2B <span class="token number">0</span>.0s <span class="token operator">=</span><span class="token operator">></span> exporting to image <span class="token number">0</span>.0s <span class="token operator">=</span><span class="token operator">></span> <span class="token operator">=</span><span class="token operator">></span> exporting layers <span class="token number">0</span>.0s <span class="token operator">=</span><span class="token operator">></span> <span class="token operator">=</span><span class="token operator">></span> writing image sha56:84b400a8fb9307f2ddc1dc29eef33c2bb664e2bdac75449d1f14e4cabaeb429 <span class="token number">0</span>.0s <span class="token operator">=</span><span class="token operator">></span> <span class="token operator">=</span><span class="token operator">></span> naming to docker.io/library/my-app <span class="token number">0</span>.0s <span class="token comment"># 플랫폼이 다를 경우에는 --platform 명령어를 사용할 것</span> $ <span class="token function">docker</span> build <span class="token parameter variable">--platform</span> linux/amd64,linux/arm64 <span class="token parameter variable">-t</span> my-app:1.0 <span class="token builtin class-name">.</span></code></pre><h3 id="8-Docker-레지스트리"><a href="#8-Docker-레지스트리" class="headerlink" title="8) Docker 레지스트리"></a>8) Docker 레지스트리</h3><ul><li>이미지 태그 지정: <code>docker tag <이미지명>:<태그> <레지스트리URL>/<이미지명>:<태그></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> tag my-app:1.0 my-registry-url/my-app:1.0</code></pre><br><ul><li>이미지 푸시: <code>docker push <레지스트리URL>/<이미지명>:<태그></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> push my-registry-url/my-app:1.0</code></pre><br><ul><li>이미지 풀: <code>docker pull <레지스트리URL>/<이미지명>:<태그></code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> pull my-registry-ur/my-app:1.0</code></pre><h3 id="9-Docker-Compose"><a href="#9-Docker-Compose" class="headerlink" title="9) Docker Compose"></a>9) Docker Compose</h3><ul><li><strong>docker-compose.yaml</strong> 파일 기반으로 컨테이너 실행: <code>docker-compose up</code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker-compose</span> up</code></pre><br><ul><li>실행 중인 docker-compose 서비스를 종료하고 네트웤, 볼륨 제거: <code>docker-compose down</code></li></ul><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker-compose</span> down</code></pre><h3 id="10-기타-유용한-명령어"><a href="#10-기타-유용한-명령어" class="headerlink" title="10) 기타 유용한 명령어"></a>10) 기타 유용한 명령어</h3><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 컨테이너 상세정보 출력</span><span class="token function">docker</span> inspect <span class="token operator"><</span>컨테이너ID<span class="token operator">></span><span class="token comment"># 로컬 이미지 크기 확인</span><span class="token function">docker</span> image <span class="token function">ls</span> <span class="token parameter variable">--format</span> <span class="token string">"{{.Repository}}: {{.Size}}"</span><span class="token comment"># docker가 사용하는 디스크 용량 확인</span><span class="token function">docker</span> system <span class="token function">df</span><span class="token comment"># 캐시 정리</span><span class="token function">docker</span> system prune</code></pre>]]></content>
<categories>
<category> ♼ DevOps </category>
</categories>
<tags>
<tag> DevOps </tag>
<tag> Container </tag>
<tag> Docker </tag>
</tags>
</entry>
<entry>
<title>Kubernetes 정리</title>
<link href="/2024/10/12/DevOps/Kubernetes/"/>
<url>/2024/10/12/DevOps/Kubernetes/</url>
<content type="html"><![CDATA[<p>어느정도 개발공부를 한 사람이라면 Kubernetes(쿠버네티스)를 최소 한 번 이상은 접해봤을 것이다.<br>유명하다면 모두가 써봤다는 얘기고, 그말은 즉슨 무조건 공부를 해야하는 것이기 때문에(…) 오늘 날을 잡아서 개념 및 명령어를 한 번 정리해봤다.</p><h2 id="1-Kubernetes란"><a href="#1-Kubernetes란" class="headerlink" title="1. Kubernetes란?"></a>1. Kubernetes란?</h2><p>쿠버네티스는 <u>컨테이너화된 애플리케이션의 <strong>배포, 스케일링, 운영</strong>을 자동화하기 위한 오픈소스</u>이다. 즉 무료로 이용할 수 있는 프로그램이다.</p><p>이름이 긴탓에 줄여서 k8s라고 하며, Google에서 개발했으나 현재 CNCF(Cloud Native Computing Foundation)에서 <a href="https://github.com/kubernetes/kubernetes">repo</a>를 관리하고 있다.</p><p>복잡한 컨테이너 환경에서 애플리케이션이 안정적으로 동작하도록 돕는 <strong>컨테이너 오케스트레이션 도구</strong>이다.</p><h2 id="2-Kubernetes의-주요-역할"><a href="#2-Kubernetes의-주요-역할" class="headerlink" title="2. Kubernetes의 주요 역할"></a>2. Kubernetes의 주요 역할</h2><ul><li>여러 컨테이너를 효과적으로 배포 및 관리</li><li>Auto Scaling: 애플리케이션의 사용량에 따라 컨테이너 수를 자동으로 늘리거나 줄임</li><li>Self-Healing: 문제가 발생한 컨테이너를 자동으로 재시작 및 복구</li><li>Load Balancing 및 트래픽 분배 -> 시스템 과부화 방지</li><li>배포 자동화: rolling update같은 방식으로 중단 없이 배포 진행</li></ul><h2 id="3-Kubernetes의-주요-구성-요소"><a href="#3-Kubernetes의-주요-구성-요소" class="headerlink" title="3. Kubernetes의 주요 구성 요소"></a>3. Kubernetes의 주요 구성 요소</h2><img src="./1.png" width="800"><h3 id="Master-Node"><a href="#Master-Node" class="headerlink" title="Master Node"></a>Master Node</h3><p>클러스터 제어 및 관리를 담당하는 중앙 서버</p><ul><li>API Server: 클러스터 진입점으로 <code>kubectl</code> 명령어를 처리</li><li>Scheduler: 컨테이너를 어떤 노드에서 실행할지 결정</li><li>Controller Manager: 리소스 상태를 관리하고 이상 상태를 복구</li><li>etcd: 클러스터 상태 정보를 저장하는 분산 키-값 저장소</li></ul><h3 id="Worker-Node"><a href="#Worker-Node" class="headerlink" title="Worker Node"></a>Worker Node</h3><p>실제로 컨테이너가 실행되는 노드</p><ul><li>Kubelet: Master Node의 명령을 받아 컨테이너를 실행하고 상태 보고</li><li>Kube Proxy: 네트워킹을 관리하고 서비스 간의 통신 지원</li><li>Container Runtime: 컨테이너 실행 환경 (Docker, containerd 등)</li></ul><h2 id="4-Kubernetes의-resource-object"><a href="#4-Kubernetes의-resource-object" class="headerlink" title="4. Kubernetes의 resource object"></a>4. Kubernetes의 resource object</h2><h3 id="1-Pod"><a href="#1-Pod" class="headerlink" title="1) Pod"></a>1) Pod</h3><ul><li>컨테이너의 최소 단위로, 하나 이상의 컨테이너를 포함</li><li>같은 Pod 내부의 컨테이너는 네트워크와 스토리지 공유</li></ul><h3 id="2-Deployment"><a href="#2-Deployment" class="headerlink" title="2) Deployment"></a>2) Deployment</h3><ul><li>Pod를 정의하고 애플리케이션의 배포 및 업데이트 관리</li><li>롤링 업데이트, 롤백 기능 지원</li></ul><h3 id="3-Service"><a href="#3-Service" class="headerlink" title="3) Service"></a>3) Service</h3><ul><li>Pod의 네트워크 접근을 제어하며, 외부 트래픽을 내부 Pod로 전달</li><li>ClusterIP, NodePort, LoadBalancer 등</li></ul><h3 id="4-ConfigMap-Secret"><a href="#4-ConfigMap-Secret" class="headerlink" title="4) ConfigMap & Secret"></a>4) ConfigMap & Secret</h3><ul><li>ConfigMap: 환경 설정 데이터 관리</li><li>Secret: 비밀번호, 인증 토큰 같은 민감한 정보를 안전하게 저장</li></ul><h3 id="5-Volume"><a href="#5-Volume" class="headerlink" title="5) Volume"></a>5) Volume</h3><p>Pod에 영구적인 데이터를 제공하기 위한 스토리지</p><h3 id="6-Namespace"><a href="#6-Namespace" class="headerlink" title="6) Namespace"></a>6) Namespace</h3><ul><li>리소스를 그룹화하여 논리적인 구획을 제공</li><li>default, kube-system 등</li></ul><h2 id="5-Kubernetes의-추가-기능"><a href="#5-Kubernetes의-추가-기능" class="headerlink" title="5. Kubernetes의 추가 기능"></a>5. Kubernetes의 추가 기능</h2><h3 id="1-Horizontal-Pod-Autoscaler-HPA"><a href="#1-Horizontal-Pod-Autoscaler-HPA" class="headerlink" title="1) Horizontal Pod Autoscaler (HPA)"></a>1) Horizontal Pod Autoscaler (HPA)</h3><p>애플리케이션의 부하에 따라 Pod의 수를 자동으로 늘리거나 줄이는 기능</p><h3 id="2-Helm"><a href="#2-Helm" class="headerlink" title="2) Helm"></a>2) Helm</h3><p>쿠버네티스 애플리케이션의 패키지 관리자. 반복적인 배포 관리</p><h3 id="3-Network-Policy"><a href="#3-Network-Policy" class="headerlink" title="3) Network Policy"></a>3) Network Policy</h3><p>네트워크 트래픽을 제어하여 Pod 간의 통신을 관리</p><h2 id="6-Kubernetes의-동작-원리"><a href="#6-Kubernetes의-동작-원리" class="headerlink" title="6. Kubernetes의 동작 원리"></a>6. Kubernetes의 동작 원리</h2><p>사용자가 kubectl을 통해 매니페스트(YAML 파일)로 리소스를 정의하고 API Server에 전달<br>-> API Server가 스케줄러를 통해 적절한 Worker Node를 선택<br>-> 선택된 Node에서 Kubelet이 컨테이너 런타임(Docker 등)을 사용해 Pod를 생성<br>-> 네트워킹, 로드 밸런싱, 스케일링 등은 서비스 및 설정에 따라 자동으로 처리</p>]]></content>
<categories>
<category> ♼ DevOps </category>
</categories>
<tags>
<tag> DevOps </tag>
<tag> Kubernates </tag>
</tags>
</entry>
<entry>
<title>[AWS] Cloud Practitioner 시험 준비 및 후기</title>
<link href="/2024/09/30/Certificate/AWS/CloudPractitioner/"/>
<url>/2024/09/30/Certificate/AWS/CloudPractitioner/</url>
<content type="html"><![CDATA[<h2 id="AWS-사전-지식"><a href="#AWS-사전-지식" class="headerlink" title="AWS 사전 지식"></a>AWS 사전 지식</h2><ul><li>있음. CloudWatch, SQS, CloudFront, S3, EC2, Transfer Family, Route53, DynamoDB, RDS(Mysql), CloudFornation 등</li><li>따라서 개념 정리는 하루만 하고 바로 시험 문제로 들어간, 일종의 bottom-up 방식으로 공부를 진행했음.<br>만약 기초가 전혀 없다면 개념 정리는 넉넉하게 일주일 정도 잡고 문제를 푸는 것을 추천함</li></ul><h2 id="시험-준비-기간-1달-인데-빡집중한거는-1주-정도"><a href="#시험-준비-기간-1달-인데-빡집중한거는-1주-정도" class="headerlink" title="시험 준비 기간: 1달 (인데 빡집중한거는 1주 정도)"></a>시험 준비 기간: 1달 (인데 빡집중한거는 1주 정도)</h2><h3 id="1-전반적인-개념-정리-하루"><a href="#1-전반적인-개념-정리-하루" class="headerlink" title="(1) 전반적인 개념 정리: 하루"></a>(1) 전반적인 개념 정리: 하루</h3><ul><li>인터넷에 요약본 정리하면 나와있음: 너무 많아서 머리가 어지러운 나머지 하루하고 때려침. 대충 반정도는 알았어서 가능했음<ul><li><a href="https://velog.io/@chan9708/AWS-Cloud-Practitioner-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EC%9A%94%EC%95%BD">블로그 1</a></li><li><a href="https://tbvjrornfl.tistory.com/188">블로그 2</a></li></ul></li></ul><h3 id="2-시험-문제-공부-덤프-무료"><a href="#2-시험-문제-공부-덤프-무료" class="headerlink" title="(2) 시험 문제 공부: 덤프 (무료)"></a>(2) 시험 문제 공부: 덤프 (무료)</h3><ul><li>덤프: 일부 문제의 답에 대해서 갑론을박이 있으며, 이는 chatGPT를 활용해서 맞춰보는 것을 추천함<ul><li><a href="https://www.examtopics.com/exams/amazon/aws-certified-cloud-practitioner">aws-certified-cloud-practitioner</a></li><li><a href="https://www.examtopics.com/exams/amazon/aws-certified-cloud-practitioner-clf-c02">aws-certified-cloud-practitioner-clf-c02</a></li><li>본인은 CLF-02로 시험을 봤으나 둘 다 무료분까지 1회독 진행함</li></ul></li><li>헷갈리는 개념들은 노트에 직접 적으며 깜지 진행. 문제는 총 1회독 진행, 하루에 1시간 씩 공부함</li></ul><p>이렇게 공부하니 대충 a4 5장 정도 나왔다.</p><h2 id="시험-준비물"><a href="#시험-준비물" class="headerlink" title="시험 준비물"></a>시험 준비물</h2><ul><li>신분증 2개(여권, 주민등록증) 또는 신분증 1개 + 신용카드를 준비해야 한다. 한글/영어 이름 확인을 위해서 이 둘은 필수!</li><li>당연하지만 시험장 내부에는 개인 필기구도 가져갈 수 없다. 오직 물품보관함 열쇠 + 신분증 + 안경(선택)만 허락한다</li></ul><h2 id="시험-팁"><a href="#시험-팁" class="headerlink" title="시험 팁"></a>시험 팁</h2><ul><li>기본적으로 영어 제공. 영어 외 언어 선택하면 해당 언어 번역본 + 20분 추가시간 주어짐 (인터넷에서는 한국어로 시험보면 30분 추가로 준다고 하던데, 언제부터인지 모르겠으나 일단 나는 20분 추가 시간 받음)</li><li>AWS Cloud Practitioner Certificate는 암기과목임. 초중고등학생 때 공부한 것처럼 외우기만 하면 됨</li><li>종종 AWS에서 온라인 스터디 여는데, 참여 시 50% 시험응시 할인 쿠폰을 받을 수 있음</li></ul><h2 id="시험-후기"><a href="#시험-후기" class="headerlink" title="시험 후기"></a>시험 후기</h2><ul><li>총 65문제 나왔고 검토는 1번해서 50분만에 나왔다. 내가 잘한다는 의미는 아니고, 찍거나 헷갈리는 문제는 전부 틀리는 사람인지라 굳이 시간낭비를 하기 싫었다 ㅎㅎ;;</li><li>좋았던 것은 시험 시간을 꼭 맞출 필요는 없다는 것이었는데, 오전 11시 시험으로 예약했으나 10시 20분에 도착해서 바로 시험에 들어갈 수 있었다.</li><li>시험은 <a href="https://naver.me/FqSi5ht7">SRTC</a>에서 봤으며, 시험장에 입장하기 전에 끄적일 수 있는 A4 한장을 주셨다. 코팅되어 있어서 제공해주시는 매직펜으로 맘껏 쓸 수 있었으며, 지우고 싶으면 손들면 된다고 하셨다. 근데 그렇게 끄적일게 없어서.. 일단 챙겨주시니 가져가서 잘 활용했다.</li><li>보안 개빡세다. 공항 몸수색 뺨치는 수준으로 본다. 바지 걷어서 양말을 보이거나, 안내자님 따라서 몸을 툭툭 두들겨야 하며, 이름 및 접수 기준 핸드폰 뒷자리 번호 등을 물어보셨다. 물론 컨닝을 할 계획이 없으니 당당하게 하면 된다.</li><li>합불여부는 시험 끝내면 바로 볼 수 있다. 몇 개 찍었는데 합격 나와서 기분이 좋은~ 참고로 시험점수는 840점이었다.</li><li>시험 난이도는.. 모르는거랑 헷갈리는거 몇 개 있었지만 덤프에 나왔던거 그대로 나온게 많아서 괜찮았다.</li><li>시험에 통과하면 다음 AWS 자격증 시험 50%를 할인 바우처 및 SME 참여 기회를 얻을 수 있다.</li></ul>]]></content>
<categories>
<category> 📝 Certificate </category>
</categories>
<tags>
<tag> Cloud </tag>
<tag> AWS </tag>
</tags>
</entry>
<entry>
<title>개발자 포트폴리오, 이렇게 준비해보자</title>
<link href="/2024/09/12/Retrospect/Portfolio/"/>
<url>/2024/09/12/Retrospect/Portfolio/</url>
<content type="html"><를 참고하면 되며, 여기서는 오픈소스 프로젝트를 지원하는 프로그램을 적어보고자 한다.<br>어차피 오픈소스에 기여할거면 ✨간지나게✨ 하는게 좋지 않은가 ㅎㅎ</p><p>여러 가지 프로그램이 있는데, 그 중 가장 추천하는 프로그램은 바로 NIPA에서 운영 중인 오픈소스 생태계 지원 사업이다.</p><p><br><br></p><h2 id="5-📁-FAQ"><a href="#5-📁-FAQ" class="headerlink" title="5. 📁 FAQ"></a>5. 📁 FAQ</h2><p>현재도 다양한 SW 프로그램에서 멘토로 활동하고 있는데, 취업 관련해서 많이 들어오는 질문들을 한 번 정리해보았다.</p><blockquote><h4 id="Q1-포트폴리오에서-가장-도움-됐던-활동은"><a href="#Q1-포트폴리오에서-가장-도움-됐던-활동은" class="headerlink" title="Q1. 포트폴리오에서 가장 도움 됐던 활동은?"></a>Q1. 포트폴리오에서 가장 도움 됐던 활동은?</h4><br>아무래도 `대회 수상 내역` 및 `프로젝트`가 가장 도움이 됐던 것 같다.<p>다만 이 두 가지 조건을 충족하면 매우 좋겠지만, 모두 알다시피 현실이 그렇게 녹록치 않다.<br>따라서 나는 <strong><code>프로젝트</code>는 필수</strong>로 들고가고, <strong><code>대회 수상 내역</code>은 선택적</strong>으로 가져가는 것을 추천한다.<br>(개인적으로 전공 비전공을 떠나, 대회 수상 자체는 95%의 운과 5%실력이 있어야 한다고 생각한다)</p></blockquote><blockquote><h4 id="Q2-직무와-관련-없는-프로젝트를-포함해도-될까-ex-백엔드-직무-프론트-c언어-관련-프로젝트"><a href="#Q2-직무와-관련-없는-프로젝트를-포함해도-될까-ex-백엔드-직무-프론트-c언어-관련-프로젝트" class="headerlink" title="Q2. 직무와 관련 없는 프로젝트를 포함해도 될까? (ex. 백엔드 직무 - 프론트, c언어 관련 프로젝트)"></a>Q2. 직무와 관련 없는 프로젝트를 포함해도 될까? <br>(ex. 백엔드 직무 - 프론트, c언어 관련 프로젝트)</h4><br><p>무조건 빼야한다. 넣어도 어차피 안본다.<br>포트폴리오는 <strong>자신의 역량을 보여주는 도구</strong>이기 때문에, 이 때 자기 어필을 제대로 하지 못할 경우 요즘 기고 나는 개발자 지망생들에게 묻히기 십상이다.</p></blockquote><blockquote><h4 id="Q3-포트폴리오-만들면서-전공과목-알고리즘-데이터베이스-등-학습법"><a href="#Q3-포트폴리오-만들면서-전공과목-알고리즘-데이터베이스-등-학습법" class="headerlink" title="Q3. 포트폴리오 만들면서 전공과목(알고리즘, 데이터베이스 등) 학습법"></a>Q3. 포트폴리오 만들면서 전공과목(알고리즘, 데이터베이스 등) 학습법</h4><br><p>이런 말이 있다. 칼을 썰면 무라도 썰자. 그냥 <code>프로젝트를 하면 된다</code>.<br>물론 맨 땅에 헤딩이라고, 프로젝트를 들어가면 ‘그래서 난 도대체 뭘 해야하지’라며 갈팡질팡하는 경우가 많다.<br>만약 내가 프로젝트를 전혀 해본 적이 없거나 시작점을 못찾겠다 싶으면 <strong>유튜브나 블로그, 각종 학습 사이트에 업로드된 프로젝트를 클론코딩 해보자.</strong><br>그게 어느 정도 익숙해졌다면 그 때부터 원하는 주제로 프로젝트를 진행하면 된다. 그러면 자연스럽게 컴퓨터 공부를 할 수 있다.</p><p>다만 <strong>알고리즘은 따로 공부가 필요하다.</strong> 기업연계가 아닌 이상 한정된 데이터를 가지고 작업을 해야하기 때문에, 우리가 생각하는 알고리즘들을 구현할 기회가 거의 없을 수 밖에 없다.</p></blockquote><blockquote><h4 id="Q4-프로젝트-수준은-어디까지"><a href="#Q4-프로젝트-수준은-어디까지" class="headerlink" title="Q4. 프로젝트 수준은 어디까지?"></a>Q4. 프로젝트 수준은 어디까지?</h4><br><p>될 수 있으면 <code>실서비스(배포)</code>까지 하는 것을 추천한다.</p><p>요즘은 포트폴리오 준비를 국비지원(KDT)에서 많이 하는데, 여기서 서버비용까지 지원해주고 있기 때문에 서비스 배포까지 하는 경우가 굉장히 많다. 이는 곧 시간이 갈 수록 스펙이 상향조준 된다는 의미며, 평범하게 했다가는 남들에게 치여 뽑히지 못할 가능성이 크다(…)</p><p>개인적으로 주니어 개발자라면 배포까지는 너무 오버스펙이라 생각하지만… 어쨌든간에 다른 경쟁자들 사이에서 남다른 모습을 보이기 위해서는 못해도 중간은 해야하기 때문에, 될 수 있으면 배포까지 하는 것을 추천한다.</p></blockquote><blockquote><h4 id="Q5-개발자는-학력을-안-본다는-말이-맞는가"><a href="#Q5-개발자는-학력을-안-본다는-말이-맞는가" class="headerlink" title="Q5. 개발자는 학력을 안 본다는 말이 맞는가?"></a>Q5. 개발자는 학력을 안 본다는 말이 맞는가?</h4><img src="/2024/09/12/Retrospect/Portfolio/2.png" class="" title="진짜 안본다고 생각하세요?"><br><p>개인적으로 지금 적고 있는 FAQ중 모든 사람들이 이 질문은 꼭 봐줬으면 싶다.<br>아니다. 본다. <code>무조건 본다</code>.</p><p>기업 공식 블로그나 KDT 입사 사례, 유튜브같은 곳을 보면, “비전공자인데 개발직군으로 취뽀해서 초봉 6000이상 받아요~”하는 내용을 쉽게 볼 수 있다.<br>이 사례들은 많은 비전공자들에게 희망을 주기도 하지만, 동시에 일부만 보고 전체를 판단하는 오류를 범하게 할 수 있다.<br>다들 은연 중에 알고 있을 것이다. 왜 이런 것들이 눈에 들어오는가. 바로 <strong>비전공자가 전공자랑 싸워서 개발직군에 취뽀한다 라는 것 자체가 굉장히 어렵기 때문</strong>이다.</p><p>기업 입장에서야 “우리는 전공/비전공을 가리지 않고 실력이 있으면 뽑습니다”라는 메시지를 전달하려는 의도로 만들었겠지만,<br>현실적으로 전공자는 다양한 교수님 및 전문 커리큘럼을 통해 체계적인 교육을 받을 기회가 많고, 비전공자는 이러한 리소스가 부족해 더 큰 노력이 필요한 편이다.<br>따라서 단순히 미디어에서 본 성공 사례만을 보고, 특히 비전공자 일수록 아무 노력도 하지 않았음에도 불구하고 ‘나도 저렇게 될거야’라고 희망만 가지다간 큰 낭패를 볼 수 있다.<br><br></p><p>그렇다면 개발자가 되기 위해 SW로 뛰어 든 비전공자들은 어떻게 해야하냐, <code>공부 기록</code>을 남기면 된다.<br>위에도 적었지만, 전공자들은 전문가에게 전문 커리큘럼으로 컴퓨터 공부를 하기 때문에 상대적으로 유리한 입장일 수 밖에 없다.<br>그리고 이 스펙은 <strong>ㅇㅇ대학교 컴퓨터공학과 졸업</strong> 같은 형식의 단 한줄로 압축해서 설명할 수 있는데 이것 또한 유리하게 적용될 수 밖에 없다.<br>한정된 글자수/페이지 내에서 자신이 할 수 있는 것들을 여러 개 나열해야 하는데, 비전공자일 경우 ‘이런 것을 했습니다’를 나열할 때<br>전공자는 ‘학과 졸업’ 하나로 기본적인 컴퓨터 공부를 했다는 것을 입증할 수 있으며 남은 공간에는 자기어필을 더 할 수 있기 때문이다.</p><p>즉, 비전공자들은 전공자들의 <strong>졸업</strong> 타이틀에 비빌 수 있는 스펙을 만들어놔야 기업에서도 수용을 하는데, 이러한 성과를 입증하는 방법은 결국 <code>무조건 기록</code>밖에 없기 때문에 무엇을 배우든 기록을 남기는 것을 추천한다.<br><br></p><p>말이 길어졌는데 정리하자면,</p><ul><li>기업은 (당연하게도) <strong>입증된 인재</strong>를 뽑길 원하며</li><li>공부 기록이 없는 이상 <strong>전문과정을 밟았음을 확실히 증명할 수 있는 전공자들이 유리한</strong> 것이 당연지사이다.</li><li>따라서 학력은 무조건 본다고 생각하면 된다.</li><li>비전공자일수록 전공자와의 격차를 줄이기 위해서 <code>공부기록</code>을 남기는 것을 추천한다.</li></ul></blockquote><blockquote><h4 id="Q6-취준은-언제부터-해야하는가"><a href="#Q6-취준은-언제부터-해야하는가" class="headerlink" title="Q6. 취준은 언제부터 해야하는가?"></a>Q6. 취준은 언제부터 해야하는가?</h4><br><p>이 글을 보는 당장 시작하는 것을 추천한다. 학년은 상관이 없다. 먼저 준비하는 사람이 빨리 승리한다.</p><p>미리 자기소개서, 프로젝트, 포트폴리오, 코딩테스트 등을 준비해봐야 나중에 봤을 때 뭐가 부족한지 판단하고 앞으로의 계획을 수월하게 세울 수 있기 때문이다.<br>이 블로그 말고도 인터넷에 좋은 내용들이 많기 때문에, 인터넷을 뒤져보고 주변 강연에도 참여해보면서 자신만의 스펙을 어떻게 뽐낼 수 있을지 충분히 고민하는 시간을 가졌으면 좋겠다.</p></blockquote><p><br><br></p><h2 id="6-🍀-마지막-하고-싶은-말"><a href="#6-🍀-마지막-하고-싶은-말" class="headerlink" title="6. 🍀 마지막 하고 싶은 말"></a>6. 🍀 마지막 하고 싶은 말</h2><ul><li>포트폴리오는 제발 겸손하게 작성하지 말자. 아주 유능한 것처럼 꾸미자</li><li>빠를수록 손해볼게 전혀 없다. 미리미리 준비하자. 뭐라도 미리 해놔야 나중에 봤을 때 고칠 수 있기 때문이다</li></ul><p><br><br></p><h2 id="7-이미지-출처"><a href="#7-이미지-출처" class="headerlink" title="7. 이미지 출처"></a>7. 이미지 출처</h2><ul><li><a href="https://www.saramin.co.kr/zf_user/">사람인</a></li><li><a href="https://www.miricanvas.com/ko">미리캔버스</a></li><li><a href="https://www.flaticon.com/kr/free-icons/pictogram">Flaticon</a></li><li><a href="https://namu.wiki/w/%EB%82%98%EB%AC%B4%EC%9C%84%ED%82%A4:%EB%8C%80%EB%AC%B8">나무위키</a></li><li><a href="https://www.oss.kr/">OSS</a></li><li>그 외 직접 제작</li></ul>]]></content>
<categories>
<category> 🎈 Retrospect </category>
</categories>
<tags>
<tag> Retrospect </tag>
<tag> Job </tag>
</tags>
</entry>
<entry>
<title>OAuth2.0를 알아보자</title>
<link href="/2024/08/20/Security/Oauth2/"/>
<url>/2024/08/20/Security/Oauth2/</url>
<content type="html"><![CDATA[<p>요즘 로그인 및 회원가입 구현 시 필수라는 Oauth2.0에 대해서 개념을 자세하게 톺아보고자 한다.</p><h2 id="OAuth란"><a href="#OAuth란" class="headerlink" title="OAuth란?"></a>OAuth란?</h2><p>풀네임으로는 Open Authorization으로 부르며, 웹 및 애플리케이션 인증 및 권한을 부여하는 개방형 표준 프로토콜이다.</p><p>설명에 앞서, 일단 유명 포털의 개발 문서에서 정의된 내용을 한 번 가져와봤다.</p><blockquote><p>이 프로토콜에서는 third-party 애플리케이션이 사용자의 리소스에 접근하기 위한 절차를 정의하고 서비스 제공자의 API를 사용할 수 있는 권한을 부여합니다.<br>대표적으로 네이버 로그인, 구글 로그인과 같은 소셜 미디어 간편 로그인이 있습니다.<br>OAuth2.0을 사용해 third-party 애플리케이션이 사용자의 소셜미디어 프로필 정보에 접근할 수 있도록 합니다.<br>B2B PRISM Live Studio 역시 OAuth2.0을 사용하여 권한을 관리하기 때문에 OAuth2.0의 기본 개념을 안내하고, 권한 부여 방법을 설명합니다.</p><p>출처: <a href="https://guide.ncloud-docs.com/docs/b2bpls-oauth2">NCP 개발 문서</a></p></blockquote><p>어느 정도 개발자 짬이 찬 사람이라면 바로 이해할 수 있겠지만, 개발에 입문한지 별로 안됐거나 개발 문서 읽는게 아직 익숙치 않은 사람들은 뭔 소린가 싶을 것이다.</p><p>쉽게 설명하자면, <code>OAuth는 다른 사이트, 앱에서 소셜 회원가입/로그인 (또는 간편 회원가입/로그인이라고 함)을 할 때 사용되는 것</code>이라고 보면 된다.<br>가령 어떤 사이트에 로그인을 할 때 <strong>Google로 로그인하기</strong>, <strong>Naver로 로그인하기</strong>가 보인다면 이 때 OAuth를 사용한다는 의미이다.</p><p><br><br></p><h2 id="OAuth-“2-0”"><a href="#OAuth-“2-0”" class="headerlink" title="OAuth “2.0”"></a>OAuth “2.0”</h2><p>인터넷에 검색해보면 대부분 <code>2.0</code>이라는 버전을 찾을 수 있는데, 현재 대중적으로 사용하는 버전이 <code>2.0</code>이기 때문이다.</p><p>즉 이전 버전(=1.0)이 있었으며, 이전 버전에서 보완할 점이 생겨서 2.0이 나왔다고 생각하면 된다.</p><p>이전에 사용한 것에서 어떤 문제가 있었는지, 그리고 최근에는 왜 2.0을 사용하는지 알아보자.</p><h3 id="1-기존-인증-방식-Basic-Authentication"><a href="#1-기존-인증-방식-Basic-Authentication" class="headerlink" title="(1) 기존 인증 방식: Basic Authentication"></a>(1) 기존 인증 방식: Basic Authentication</h3><p>어쨌든간에 개발자 입장에서 로그인 한 유저에게 프로그램 접근 권한을 주기 위해서는 인증이 된 유저라고 표시를 해야하긴 한다.</p><p>따라서 기존에는 Basic Authentication (흔히 Auth라고 부름)에 따라, 애플리케이션에 사용자의 아이디 & 비밀번호를 직접 제공해 접근 권한을 줬다.</p><p>하지만 다음의 문제가 항상 거론되었고, 이 때문에 OAuth 1.0이 생겨났다.</p><ul><li>보안 취약성: 클라이언트가 사용자의 자격 증명을 저장하거나 전송하는 과정에서 유출 위험</li><li>권한 관리 불가: 특정 자원에 대한 제한적인 권한 부여가 불가능. (모든 권한이 열림)</li><li>복잡성 증가: 사용자 비밀번호가 변경되면, 이를 사용하는 모든 서비스에서 수정해야 함</li></ul><p>한줄 요약하면 <strong>해킹 당하면 모든 정보가 털려서 문제</strong>라는 의미이다.</p><h3 id="2-OAuth-1-0의-등장-및-제한점"><a href="#2-OAuth-1-0의-등장-및-제한점" class="headerlink" title="(2) OAuth 1.0의 등장 및 제한점"></a>(2) OAuth 1.0의 등장 및 제한점</h3><h5 id="개발-배경"><a href="#개발-배경" class="headerlink" title="개발 배경"></a>개발 배경</h5><p>소프트웨어는 나날이 발전하고 있고, 이 때문에 사람들은 보안에 대해 깊게 생각하게 되었다.<br>그래서 나온 개념이 바로 OAuth 1.0이다.</p><p>개발자 입장에서는 보안을 되게 민감하게 받아들여야 하고, 하지만 관리가 매우 빡세다보니 구글이나 네이버같이 <strong>(주로) 대기업에서 이미 구현해둔 인증 로직을 가져와서 사용</strong>하는 것이다.<br>대기업에서 구현한 인증 로직은 아무래도 중소규모 기업들에 비해 보안 관리가 빡셀 것이며, 만약에 정보가 털렸다고 해도 책임이 대기업에게 있지 우리같은 소시민에게는 없기 때문이다.<br><del>폭탄 돌리기</del><br><del>물론 구현 잘못해서 정보 노출되게 코딩하면 우리 잘못이긴 하다</del></p><p>물론 그 외에도, 이런 인증 로직 하나 구현하는게 좀 빡센 편이라 이미 구현한거 가져와서 개발 시간을 아끼는겸 사용하기도 한다.</p><h5 id="Auth와-다른-점"><a href="#Auth와-다른-점" class="headerlink" title="Auth와 다른 점"></a>Auth와 다른 점</h5><p>OAuth 1.0에서는 남이 구현한 사용자 인증 로직을 가져오는 것이기 때문에, 내가 해당 인증 로직을 가져와도 되는가를 증명할 수단이 필요하다. 이것이 바로 <code>HMAC-SHA1 (서명 기반 인증)</code>이다.<br>이 로직을 이용하면 기존에 구현된 인증 로직을 바탕으로, 아이디 및 비밀번호를 넣으면 접근 권한을 받아올 수 있다.</p><h5 id="한계점"><a href="#한계점" class="headerlink" title="한계점"></a>한계점</h5><ul><li>복잡한 프로세스: 요청마다 일일이 서명 생성 및 검증이 필요 -> 구현 및 디버깅이 어려우며, 개발자에게 너무 많은 학습을 요구함</li><li>확장성 부족: 특정 인증/권한 시나리오에 맞춰 설계되어 다양한 클라이언트 유형(SPA, 모바일 애플리케이션 등)에 적용하기 어려움</li><li><strong>TSL 의존성 부족</strong>: 사실 상 원인<ul><li>서명으로 보안은 강화했으나, TLS를 필수로 요구하지 않아 <strong>Man-in-the-Middle</strong>에 매우 취약적</li><li>(참고) Man-in-the-Middle(중간자 공격): 두 당사자 간의 통신을 가로채서 조작 및 엿듣는 사이버 공격 방식<blockquote><p>(위치 선정)<br>공격자는 클라이언트(사용자)가 사용하고 있는 wifi에 접속함<br>그리고 클라이언트와 서버 사이의 통신 경로에 잠복</p><p>(통신 가로채기)<br>이 때 클라이언트가 서버에게 요청을 보냄<br>서버에 도달하기 전에 해당 요청을 공격자가 가로챔<br>클라이언트가 요청한 정보를 수정하거나 읽고 서버에 전달</p><p>(응답 가로채기)<br>마찬가지로, 서버에서 클라이언트에게 보낸 응답도 공격자가 가로챔<br>서버가 보낸 응답 내용을 수정하거나 읽고 클라이언트에게 전달</p></blockquote></li></ul></li></ul><h3 id="3-OAuth-2-0의-등장"><a href="#3-OAuth-2-0의-등장" class="headerlink" title="(3) OAuth 2.0의 등장"></a>(3) OAuth 2.0의 등장</h3><p>이렇게 OAuth 1.0의 한계를 극복하고자 나온 것이 바로 OAuth 2.0이다.</p><ul><li>프로세스 간소화: 서명 대신 <code>Access Token</code>을 사용해서 요청을 단순화</li><li>확장성 및 표준화<ul><li>확장 가능한 구조로, 추가적인 인증 메커니즘(JWT, OpenID Connect 등)과 쉽게 통합 가능</li><li>다양한 플랫폼과 서비스(Google, Facebook, GitHub 등)에서 표준으로 채택</li><li>다양한 클라이언트 유형 지원<ul><li>다양한 Grant Type 제공:<ul><li>Authorization Code Grant: 서버 사이드 애플리케이션.</li><li>Implicit Grant: 브라우저 기반 애플리케이션.</li><li>Client Credentials Grant: 서버 간 통신.</li><li>Resource Owner Password Grant: 신뢰할 수 있는 클라이언트.</li></ul></li><li>각 시나리오에 맞는 유연한 인증 방식 제공.</li></ul></li></ul></li><li>TSL 필수 사용: 데이터 전송 중에 보안을 보장하여 데이터의 무결성 입증 가능</li></ul><br><p>기능이 개선이 되었지만, 이것이 그렇다고 ✨만능✨인 것은 아니다.<br>현재 크게 두 가지 단점이 있는데, 바로</p><ul><li>초기 설정이 까다롭고</li><li><strong>Access Token</strong>이 유출되면 악용될 가능성이 매우 높아지기 때문이다.</li></ul><p>이 또한 언젠가 해결해야 하는 문제로 토론이 이루어지고 있으나, 현재까지로는 토큰만 유출 안되면 이론상 완벽하기 때문에 다들 바로 표준으로 채택한 것이다.<br>따라서 2.0이 2012년에 나왔는데, 현재까지 3.0은 나오지 않은 상태이다.</p><img src="./2.png" width="800"><h3 id="4-OAuth-2-0-주요-용어"><a href="#4-OAuth-2-0-주요-용어" class="headerlink" title="(4) OAuth 2.0 주요 용어"></a>(4) OAuth 2.0 주요 용어</h3><ul><li>Resource Owner (자원 소유자)<ul><li>자원의 주인. 일반적으로 사용자를 지칭함</li><li>ex) Facebook 계정을 가진 사용자</li></ul></li><li>Client (클라이언트)<ul><li>자원 소유자가 허용한 자원에 접근하려는 애플리케이션</li><li>ex) 자신의 Google Drive에 파일을 업로드하는 서비스</li></ul></li><li>Authorization Server (인증 서버)<ul><li>클라이언트가 인가 코드 또는 토큰을 요청하는 서버</li><li>예: Google, Facebook 등</li></ul></li><li>Resource Server (자원 서버)<ul><li>보호된 자원이 저장된 서버</li><li>예: Google Drive API 서버</li></ul></li><li>Access Token (액세스 토큰)<ul><li>클라이언트가 자원 서버에 접근할 때 사용하는 임시 키</li><li>만료 시간이 있음</li></ul></li></ul><h3 id="5-OAuth-2-0-작동-방식"><a href="#5-OAuth-2-0-작동-방식" class="headerlink" title="(5) OAuth 2.0 작동 방식"></a>(5) OAuth 2.0 작동 방식</h3><img src="./1.png" width="500"><p>출처: <a href="https://developers.google.com/identity/protocols/oauth2?hl=ko">GCP 개발 문서</a></p><ol><li><strong>Request token</strong><ul><li>사용자(클라이언트)는 로그인을 시도함. 이 때 Google이나 Naver같은 소셜 로그인을 선택함 = 사용자는 사용자 권한을 얻기 위해 인증을 받아야함</li><li>이때 사용하는 토큰은 업체마다 구현이 다름: 개발문서를 꼭 참고할 것</li></ul></li><li><strong>Authorization code</strong><ul><li>사용자가 인증 서버에서 로그인을 완료하면, 인증 서버는 Authorization code를 redirect url로 전달함</li><li>이 때 code는 임시로, 시간이 지나면 다시 재발급을 받아야 함</li></ul></li><li><strong>Exchange code for token & Token response</strong>: code를 이용해서 curl 요청을 보냄으로써 <code>Access token</code>을 발급함</li><li><strong>Use token to call API</strong><ul><li>API를 요청할 때, Access token을 사용해서 사용자 정보를 인증함</li><li>설명을 위해 로그인을 예시로 들었기 때문에, 여기서는 login API가 사용될 것이고 인증이 잘 됐다면 바로 로그인이 될 것임</li></ul></li></ol>]]></content>
<categories>
<category> 🔐 Security </category>
</categories>
<tags>
<tag> Security </tag>
<tag> Auth </tag>
<tag> OAuth2 </tag>
</tags>
</entry>
<entry>
<title>Container 정리</title>
<link href="/2024/07/12/DevOps/Container/"/>
<url>/2024/07/12/DevOps/Container/</url>
<content type="html"><![CDATA[<p>개발을 하다보면 아무래도 docker를 많이 쓰게 될텐데, 그 때 컨테이너 어쩔저쩔~~ 이라는 개념이 나오면서 사람들의 머리가 아파지기 시작한다.<br>따라서 컨테이너에 대해 개념을 잡고 싶은 사람이라면 이 글을 잘 읽어보길 추천한다. 배포 세팅(특히 devops, cloud native)에서 많이 언급되는 개념이기 때문이다.</p><h2 id="1-Container란"><a href="#1-Container란" class="headerlink" title="1. Container란"></a>1. Container란</h2><p>컨테이너는 애플리케이션과 필요한 라이브러리, 종속성을 하나로 묶어 어디서나 실행할 수 있는 일종의 <strong>가벼운 실행 환경</strong>이다.</p><p>따라서 특정 OS나 환경에 의존하지 않고 동일하게 실행된다는 의미다.</p><blockquote><p>쉽게 풀어보자면, 프로젝트를 진행하는데 A는 Windows에서, B는 MacOS에서 진행한다고 쳐보자.<br>따라서 각 환경마다 세팅이 다르기 때문에 각자 라이브러리 및 종속성을 같은 세팅으로 다운받더라도 오류가 날 수 있다는 의미다.<br>이 때 컨테이너를 사용하면 OS가 다름에도 불구하고 동일하게 프로그램이 실행된다. </p></blockquote><h2 id="2-Container에서-사용하는-중요-개념"><a href="#2-Container에서-사용하는-중요-개념" class="headerlink" title="2. Container에서 사용하는 중요 개념"></a>2. Container에서 사용하는 중요 개념</h2><p>말로만 적으면 이해가 어렵기 때문에 시나리오를 하나 정의해서 설명을 해보도록 하겠다.</p><blockquote><p><code>상황</code></p><ul><li>A: Windows 사용자, B: Macbook 사용자</li><li>이 둘은 팀플로 <strong>Python Flask 웹 애플리케이션</strong>을 개발 중</li><li>A는 로컬 Windows에서, B는 로컬 Macbook에서 작업 중임 = 프로젝트 실행 환경이 다름</li><li>A는 컨테이너 기술을 사용하여 동일한 환경에서 실행될 수 있도록 설정하고 이를 B에게 공유하려고 함</li></ul></blockquote><table><thead><tr><th align="center">개념</th><th align="center">설명</th><th align="center">예시</th></tr></thead><tbody><tr><td align="center">Container</td><td align="center">애플리케이션 실행 환경</td><td align="center">- A는 Python Flask 애플리케이션을 실행하기 위해 컨테이너를 사용함<br>- B는 A가 만든 컨테이너를 그대로 실행하면 동일한 환경에서 앱이 실행됨</td></tr><tr><td align="center">Container Image</td><td align="center">실행 환경을 템플릿으로 저장</td><td align="center">- A는 컨테이너 실행을 위한 환경(Python, Flask, 그 외 기타 requirements.txt 의존성 모듈)을 이미지로 저장</td></tr><tr><td align="center">Container Runtime</td><td align="center">컨테이너를 실행 및 관리</td><td align="center">- A와 B 모두 컨테이너 런타임을 사용하여 컨테이너를 실행</td></tr><tr><td align="center">Container Orchestration</td><td align="center">여러 컨테이너의 배포, 스케일링, 관리 등을 자동화</td><td align="center">- A와 B의 프로젝트가 성장하며 트래픽이 증가하면, 오케스트레이션 도구로 컨테이너를 확장</td></tr><tr><td align="center">Namespaces</td><td align="center">컨테이너의 리소스(CPU, 메모리 등) 격리</td><td align="center">- Flask 웹 컨테이너 / PostgreSQL 데이터베이스 컨테이너 / Redis 캐시 컨테이너 (각각 분리)<br>- 컨테이너별로 독립된 네트워크 스택(IP주소, 포트, 라우팅 테이블 등)을 가짐<br>- A의 로컬 시스템에서 실행 중인 프로세스를 확인할 때, 컨테이너의 내부 프로세스를 검색할 수 없음</td></tr><tr><td align="center">CGroups(Control Groups)</td><td align="center">리소스 사용량 제한</td><td align="center">- Flask 웹 컨테이너 / PostgreSQL 데이터베이스 컨테이너 / Redis 캐시 컨테이너 (각각 분리)<br>- A가 컨테이너를 생성할 때 한 쪽 컨테이너로만 너무 많이 리소스를 사용하지 않도록 제어</td></tr><tr><td align="center">Networking</td><td align="center">컨테이너 간 외부 통신</td><td align="center">- Flask 웹 컨테이너 / PostgreSQL 데이터베이스 컨테이너 / Redis 캐시 컨테이너 (각각 분리)<br>- A와 B는 웹 컨테이너와 데이터베이스 컨테이너를 연결하여 사용</td></tr><tr><td align="center">Registry</td><td align="center">컨테이너 이미지를 저장 및 배포</td><td align="center">- A는 컨테이너 이미지를 Hub에 업로드하여 B와 공유<br>- B는 Hub에서 이미지를 다운받아 사용</td></tr></tbody></table><h2 id="3-Container-VS-VM-Virtual-Machine"><a href="#3-Container-VS-VM-Virtual-Machine" class="headerlink" title="3. Container VS VM(Virtual Machine)"></a>3. Container VS VM(Virtual Machine)</h2><table><thead><tr><th align="center">Container</th><th align="center">Virtual Machine</th></tr></thead><tbody><tr><td align="center">Kernal 공유</td><td align="center">Hypervisor 기반</td></tr><tr><td align="center">빠른 속도, 가벼움, 이식성</td><td align="center">높은 격리성, 완전한 OS 제공</td></tr></tbody></table><h3 id="Kernal-커널-서로-사이좋게-양보하며-컴퓨터-자원-공유"><a href="#Kernal-커널-서로-사이좋게-양보하며-컴퓨터-자원-공유" class="headerlink" title="Kernal (커널): 서로 사이좋게 양보하며 컴퓨터 자원 공유"></a>Kernal (커널): 서로 사이좋게 양보하며 컴퓨터 자원 공유</h3><ul><li>OS의 핵심 부분으로, HW와 SW간의 다리 역할</li><li>애플리케이션이 하드웨어 자원(CPU, 메모리, 디스크, 네트워크 등)을 사용할 수 있도록 중개</li></ul><blockquote><p><code>상황</code> </p><ul><li>C, D, E는 각각 Flask, PostgreSQL, Redis로 실행 중</li><li>CPU는 4코어, 메모리는 8GB로 설정</li></ul><p><code>예시</code></p><ul><li>C가 러닝 중일 때 (D와 E는 유휴 상태):<ul><li>C(Flask) 가 모든 CPU와 메모리를 사용</li><li>다른 컨테이너(D, E)가 비활성 상태이므로 커널은 자원을 C에 최대한 배분</li></ul></li><li>D(PostgreSQL) 러닝 시작: 커널이 자원을 동적으로 조정<ul><li>C(Flask)** 가 CPU 2코어, 메모리 4GB를 사용하도록 줄임</li><li>D(PostgreSQL)*에게 CPU 2코어, 메모리 4GB를 배분</li><li>자원이 부족할 경우 <strong>cgroups(Control Groups)</strong> 설정에 따라 특정 컨테이너(C 또는 D)의 자원을 제한</li></ul></li><li>D와 E가 동시에 러닝 시작: 자원이 더 많이 나눠짐<ul><li>C(Flask): CPU 1코어, 메모리 3GB</li><li>D(PostgreSQL): CPU 2코어, 메모리 3GB</li><li>E(Redis): CPU 1코어, 메모리 2GB</li></ul></li></ul></blockquote><h3 id="Hypervisor-하이퍼바이저-사이가-안좋아서-자원을-빌리되-서로-공유하지-않음"><a href="#Hypervisor-하이퍼바이저-사이가-안좋아서-자원을-빌리되-서로-공유하지-않음" class="headerlink" title="Hypervisor (하이퍼바이저): 사이가 안좋아서 자원을 빌리되 서로 공유하지 않음"></a>Hypervisor (하이퍼바이저): 사이가 안좋아서 자원을 빌리되 서로 공유하지 않음</h3><ul><li>가상화를 위한 소프트웨어</li><li>물리적 하드웨어(= 실제 컴퓨터)에서 여러 개의 VM을 실행할 수 있게함</li></ul><blockquote><p><code>상황</code></p><ul><li>C, D, E는 각각 Windows, Ubuntu, CentOS로 실행 중<br>CPU는 8코어, 메모리는 16GB로 설정</li></ul><p><code>예시</code></p><ul><li>C가 실행 중일 때 (D와 E는 비활성 상태):<ul><li>C(Windows)가 CPU 8코어, 메모리 16GB를 사용</li><li>다른 VM(D, E)이 비활성 상태이므로 하이퍼바이저는 모든 자원을 C에 배분</li></ul></li><li>D(Ubuntu)가 실행을 시작하면: 하이퍼바이저가 자원을 동적으로 조정<ul><li>C(Windows): CPU 4코어, 메모리 8GB</li><li>D(Ubuntu): CPU 4코어, 메모리 8GB</li><li>각 VM은 독립된 OS와 애플리케이션을 실행하며 서로 영향을 주지 않음</li></ul></li><li>D와 E(CentOS)가 동시에 실행을 시작하면: 자원이 세 VM으로 나눠짐<ul><li>C(Windows): CPU 3코어, 메모리 6GB</li><li>D(Ubuntu): CPU 3코어, 메모리 6GB</li><li>E(CentOS): CPU 2코어, 메모리 4GB</li><li>각 VM은 독립된 OS와 애플리케이션을 실행하며 서로 영향을 주지 않음<br>=> 많아지면 컴퓨터 속도 저하 원인</li></ul></li></ul></blockquote><h2 id="4-Container-도구들"><a href="#4-Container-도구들" class="headerlink" title="4. Container 도구들"></a>4. Container 도구들</h2><p>(하나씩 글 작성 ing. 작성할 때마다 링크 추가 예정)</p><h3 id="Container-Runtime"><a href="#Container-Runtime" class="headerlink" title="Container Runtime"></a>Container Runtime</h3><ul><li>Docker: 컨테이너 생태계를 주도한 대표적인 런타임</li><li>Containerd: Docker의 핵심 런타임으로 분리되어 Kubernetes에서 직접 사용 가능</li><li>Podman: 데몬리스 컨테이너 실행환경으로 Docker의 대체제</li><li>CRI-O: Kubernetes와 호환되는 경량 런타임</li></ul><h3 id="Container-Ochestration"><a href="#Container-Ochestration" class="headerlink" title="Container Ochestration"></a>Container Ochestration</h3><ul><li>Kubernetes: 가장 널리 사용되는 컨테이너 오케스트레이션 도구. 자동 스케일링, 복구, 배포 지원</li><li>Docker Swarm: 간단한 오케스트레이션에 적합한 Docker 내장 도구</li><li>Nomad: HashiCorp에서 개발한 경량 오케스트레이션 도구</li></ul><h3 id="Container-Image"><a href="#Container-Image" class="headerlink" title="Container Image"></a>Container Image</h3><ul><li>Buildah: Docker 없이 이미지를 빌드 가능</li><li>Kaniko: 클라우드 환경에서 안전하게 이미지 빌드</li></ul><h3 id="컨테이너-보안-도구"><a href="#컨테이너-보안-도구" class="headerlink" title="컨테이너 보안 도구"></a>컨테이너 보안 도구</h3><ul><li>Falco: 실시간 컨테이너 보안 이벤트 감지</li><li>Aqua Security: 컨테이너 환경 보안을 포괄적으로 관리</li></ul>]]></content>
<categories>
<category> ♼ DevOps </category>
</categories>
<tags>
<tag> DevOps </tag>
<tag> Container </tag>
</tags>
</entry>
<entry>
<title>[컨퍼런스] Tech Summit Silicon Valley 2024 후기 - 2일차</title>
<link href="/2024/06/22/Conference/24-silicon-valley-2/"/>
<url>/2024/06/22/Conference/24-silicon-valley-2/</url>
<content type="html"><![CDATA[<h2 id="1-PANEL-SESSION-The-Future-of-Tech-Startups-Trends-and-Predictions"><a href="#1-PANEL-SESSION-The-Future-of-Tech-Startups-Trends-and-Predictions" class="headerlink" title="1. PANEL SESSION: The Future of Tech Startups: Trends and Predictions"></a>1. PANEL SESSION: The Future of Tech Startups: Trends and Predictions</h2><img src="./1.jpeg" width="500"><p>여기서는 유명 대기업 테크기업부터 스타트업까지 모두 모여서 이야기를 진행했다.<br>주제가 주제다보니까 당연하게도 AI와 연관되어 질문이 많이 나왔는데, 내용은 대략적으로 이렇게 있었다.</p><ul><li>AI가 발전하는 과정에서, 우리는 앞으로 정확한 솔루션/철저한 보안 관련 쪽으로 신경을 많이 써야한다.</li><li>데이터의 퀄리티가 올라가고 있기 때문에 갈수록 비싸질 것이다. 모델도 그 비싼 데이터로 운영되기 때문이다.</li><li>Intrastructure적으로는 챌린지가 될 것이다. 여기도 결국 돈이 연관되어 있기 때문이다.</li><li>AI라고 무턱대고 도전하는건 지양한다. 예전에 암호화폐 유행했었을 때도 다들 그쪽으로 뛰어들었는데, 결과가 많이 좋지 않았다. 어느 정도 잘 알아보고 하는게 좋을 것 같다.</li><li>Founder라면 무조건 공부가 필요하다. 이것저것 잡다하게 최대한 공부해야한다. 그리고 솔루션이 scalable한지, 싼지 알아봐야 한다.</li></ul><h2 id="2-Unraveling-Tomorrow-Mapping-the-Next-20-Years-of-Technological-Evolution"><a href="#2-Unraveling-Tomorrow-Mapping-the-Next-20-Years-of-Technological-Evolution" class="headerlink" title="2. Unraveling Tomorrow: Mapping the Next 20 Years of Technological Evolution"></a>2. Unraveling Tomorrow: Mapping the Next 20 Years of Technological Evolution</h2><img src="./2.jpeg" width="500"><p>이 세션에서는 IoT, Bio, Eco, VR/AR 등 다양한 필드에서의 변화 및 전망을 발표했다.<br>여기서도 결국 하는 얘기는 같았는데, 데이터가 점점 쌓일수록 퀄리티가 올라가며, 가격이 비싸질 것이라는 얘기였다.</p><p>여기서는 세션 제목에 Technological이 붙어서 그런지 개발자만 참여하는 기적(?)이 일어나서 기술적으로 깊게 토론하는 시간을 가질 수 있었다.</p><ul><li>IoT쪽도 지금 AI를 도입하는 중이다. 근데 여기도 Cloud 환경을 이용하는 경우가 많기 때문에 Infrastructure쪽으로 일단 cost조절을 최우선으로 두고 개발하고 있다.<ul><li>Cloud는 AWS가 가장 편한데 비싸다. 차라리 GCP를 사용하는게 더 낫다. AWS보다는 접근성이 좀 떨어지는데 가격면에서는 좋다.</li><li>근데 IaC가 나오면서 AWs, GCP, Azure도 사용하기 편해졌다. Terraform을 사용하면 이 모든 것들을 관리할 수 있다. 아직 이걸 다루는 사람이 부족한 편인 것 같더라.</li><li>Terraform 자체를 사용하는 것은 좋은 아이디어인데, 그게 오픈소스다보니까 불안정한면이 있긴 하다. 그런데 AI기반 산업발전속도가 빠르다보니 오픈소스가 안정화되기 전에 치고 올라가버리니 사용이 애매하다. -> 그건 어쩔 수 없는 것 같다. 그저 우리가 최대한 기여하는 수밖에 없다. 원래 여기 업계는 빨리 변하지 않느냐</li></ul></li></ul><h2 id="3-How-AI-is-transforming-skill-development-and-validation-of-the-future-of-work"><a href="#3-How-AI-is-transforming-skill-development-and-validation-of-the-future-of-work" class="headerlink" title="3. How AI is transforming skill development and validation of the future of work"></a>3. How AI is transforming skill development and validation of the future of work</h2><img src="./3.jpeg" width="500"><p>이 세션은 재밌게 봤는데, 발표자분이 NLP에 10년 넘게 종사하신 분이었기 때문이다.<br>자신이 개발한 기술을 예시로 NLP가 어떻게 발전했는지 얘기했는데, 내가 NLP 프로젝트를 하면서 놓쳤던 부분에 대해 자연스럽게 짚어주시는걸 보고 신기하다는 느낌을 받았다.<br>참고로 이분이 강조하신 것도 다른 세션과 거의 동일했다.</p><ul><li>앞으로는 인공지능 퀄리티가 더 좋아질 것이다. 즉 서비스 제공 질이 더 좋아질 것이다.</li><li>즉 우리는 이거를 어떻게 “활용”하는지에 관건이 달렸다.</li><li>개발자 입장에서도 어떻게 퀄리티 높은 모델을 제공할건지 고민해야 할 필요성이 있다.<br>일단 좋은 모델을 만들기 위해서는 좋은 데이터가 필요하며, 이 과정에서 데이터 전처리의 중요성이 더 커지고 있다.</li><li>투자자도 공부를 해야한다. AI/개발에 대해 전혀 모르는 상태에서 투자할 것이 아니라, 데이터의 퀄리티가 어떤지를 파악하는 눈을 길러야 한다. 많은 기업들에서 쓰레기 데이터로 학습을 시켜 돈을 낭비하는 경우가 많은데, 이를 알아봐야 한다.<ul><li>GPT기술이 많이 발전했기 때문에 이 모델들에게 물어보면 어느 정도 사전 지식을 얻을 수 있을 것이다.</li></ul></li><li>(정리) 앞으로 인공지능 산업은 모델이 아닌 “퀄리티”로 승패를 나눌 것이다. 이 과정에서 데이터 전처리/자동화의 중요성은 더 커질 것이다.</li></ul><h2 id="2일차-후기"><a href="#2일차-후기" class="headerlink" title="2일차 후기"></a>2일차 후기</h2><ul><li><p>이날따라 AI에 대한 깊은 토론이 진행됐던 것 같다. 공식 석상에서 기술적인 얘기는 안했다만, 다들 한결같이 “데이터 전처리”, “자동화”에 강조를 두는 것을 보면 앞으로의 AI개발에 이를 눈여겨봐야 할 것 같다는 생각이 들었다.</p></li><li><p>이 날도 1일차와 동일하게 세션이 끝나고 개발자들끼리 모여서 대화의 장을 펼쳤다.<br>영어로 대화했기 때문에 정신집중 하느라 너무 피곤했는데, 다들 그거 감안하고 나한테 말을 걸어주더라ㅋㅋㅋㅠㅠㅠ 참 따뜻한 사람들이다 ㅎㅎㅎ</p><p>대화 내용을 요약하면 아래와 같다.</p><ul><li>이 날은 데이터에 대해서 심도 있게 대화를 나눴는데, 사람 사는게 다 거기서 거기인지 생각이 동일한 곳이 많았다.<br>대표적으로 데이터 정제에 관해서였는데, 데이터 정제가 까다롭다보니 기술+시간 비용이 크게 드는 편이라고 한다.<br>그런데 비전공자들(특히 비전공자 CEO같은 사람들)이 이것조차 모르는 상태에서 투자를 계속하다보니 전체 투자금의 30% 이상을 엉뚱한 곳에 돈을 사용하면서 최적의 결과물을 요구한다며 한숨을 쉬더라.</li><li>어쨌든간에 오픈소스 면에서는 데이터 정제 쪽으로 코드가 있으면 여러모로 SW산업에 긍정적인 영향을 미칠 것 같다는 얘기가 나왔다.</li><li>그 외로는 서로 회사에 관한 얘기와 앞으로의 핫이슈 프레임워크/라이브러리에 대한 토론을 나눴다. 내가 모르는 오픈소스들을 많이 접할 수 있어서 한층 더 똑똑해진 기분이 들었다.</li></ul></li><li><p>평일에 열리다보니 다들 회사 <-> 컨퍼런스를 오가느라 행사가 생각보다 시끌벅쩍하지는 않았다. 다만 시간대마다 다양한 사람들을 만날 수 있어서 재밌었던 것 같다. (체감상으로는 한 1000명 정도 온듯..?)<br>다음에 기회가 된다면 또 가서 사람들이랑 대화를 나눠보고 싶은 생각이 들었다 😁</p></li></ul>]]></content>
<categories>
<category> ✈️ Conference </category>
</categories>
<tags>
<tag> Conference </tag>
</tags>
</entry>
<entry>
<title>[컨퍼런스] Tech Summit Silicon Valley 2024 후기 - 1일차</title>
<link href="/2024/06/21/Conference/24-silicon-valley-1/"/>
<url>/2024/06/21/Conference/24-silicon-valley-1/</url>
<content type="html"><![CDATA[<h2 id="그-많고-많은-컨퍼런스-중-실리콘밸리를-선택한-이유는"><a href="#그-많고-많은-컨퍼런스-중-실리콘밸리를-선택한-이유는" class="headerlink" title="그 많고 많은 컨퍼런스 중 실리콘밸리를 선택한 이유는?"></a>그 많고 많은 컨퍼런스 중 실리콘밸리를 선택한 이유는?</h2><p>개발자들의 세계 최고의 필드 “실리콘밸리”에서 열리는 컨퍼런스라는 것도 의미가 있었지만, 나는 그 무엇보다 <u>실리콘밸리 개발자들과 최신 기술 동향에 대해 ‘네트워킹’</u>을 해보고 싶어서 참석하게 되었다.</p><p>필자는 작년에 영어공부 플랫폼을 통해 실리콘밸리에 초청받아 구글, 우버, 메타 등 다양한 기업들을 투어해봤는데, <u>점심/저녁을 먹거나 심지어 쉬는 시간일 때조차 실리콘밸리 개발자들은 매우 열정적으로 어떤 주제에 대해 토론</u>하는 모습이 참 인상 깊었기 때문이다.</p><p>그 당시 언젠가는 대화하겠지라며 사람들에게 특별히 말을 걸지는 않고 넘어갔는데, <a href="https://www.oss.kr/open_up_intro">OpenUp</a>에서 최대 250만원까지 컨퍼런스 지원을 해준 덕에 1년 뒤인 지금 바로 꿈을 이루게 되었다 (?!)</p><p>참고로 이번에 참여한 컨퍼런스 이름은 바로 <a href="https://techsummit.tech/san-francisco/">Tech Summit Silicon Valley</a> 였다.</p><img src="./1.png" width="500"><br><h2 id="실리콘밸리에-갈-때-참고할-점"><a href="#실리콘밸리에-갈-때-참고할-점" class="headerlink" title="실리콘밸리에 갈 때 참고할 점"></a>실리콘밸리에 갈 때 참고할 점</h2><p>원래 후기를 컨퍼런스 내용에 대해서만 적으려고 했으나, 컨퍼런스 장소가 <u>외국 + 시골</u>인 점을 감안해서 한 번 작성을 해보도록 한다.<br>실리콘밸리에 가게 된다면 아래 내용을 참고하자.</p><ul><li>실리콘밸리 = 깡시골<ul><li>물론 우리나라 시골에 비해 버스와 지하철이 잘 되어 있으나, 결국 시골은 시골이다. 하물며 도시 대중교통도 잘 연착되는 편인데, 시골이면 오죽할까 (…)<br>돈 아끼려고 대중교통 이용하다가 오히려 약속에 늦는 비상사태가 벌어질 수 있다.</li><li>게다가 “미국”의 시골이다. 미국 땅덩어리와 우리나라를 비교하면 절대 안된다. 미국은 모든 곳이 차가 없으면 돌아다니기가 참 힘들다. 차로 10분인 거리가 도보로는 1시간이 걸리는 곳이다.</li><li>될 수 있으면 샌프란시스코 국제공항에서 내리자마자 렌트카를 빌리자. 생각보다 싸다. 그리고 미국의 교통 법규는 우리나라와 거의 동일하다.<ul><li>“비보호 좌회전, STOP표지판, 스쿨버스”가 가장 큰 차이점인데, 이거는 유튜브 영상 몇 개 보면 쉽게 이해할 수 있다.</li><li>도로가 매우 넓으며, (고속도로에 한해) 속도 제한도 한국보다 너그러운 편이라 쉽게 적응할 수 있다.</li><li>이 때 구글 지도로 네비게이션을 이용하면 된다.</li></ul></li></ul></li><li>미국 동부보다 영어에 대한 거부감이 적은 편<ul><li>필자의 개인적인 생각이다만, 능력있는 사람들이 모여있는 곳이다보니 영어권이 아닌 사람들이 많다.</li><li>그만큼 다양한 영어 발음을 구사해 서로의 영어를 못알아듣는 경우가 많다. 그래서 영어로 어떻게든 대화를 하려고 한다면 (동부보다 비교적) 상대방을 더욱 존중하고 이해하려는 모습을 보여준다.</li><li>영어에 두려움을 갖지 말고 자신이 무슨 말을 하고싶은지 어필하자. 다들 열심히 들어주고 얘기해줄 것이다.</li></ul></li></ul><p>그 외 나머지는 인터넷에 “미국 여행”같은 키워드를 검색해서 찾아보면 될 것이다.</p><p><br><br><br></p><p>서두는 이렇게 마무리하고, 본격적으로 컨퍼런스 후기를 작성해보도록 하겠다.<br>이번 포스트에서는 6월 19일에 진행된 컨퍼런스 내용을 바탕으로 작성해봤다.</p><img src="./2.jpeg" width="300"><p><del>막상 글 작성하니 이 사진 넣을 곳이 없어서 여기다가 낑겨본다</del></p><hr><h2 id="1-Using-technology-as-a-way-to-enable-financial-inclusion-in-emerging-markets"><a href="#1-Using-technology-as-a-way-to-enable-financial-inclusion-in-emerging-markets" class="headerlink" title="1. Using technology as a way to enable financial inclusion in emerging markets"></a>1. Using technology as a way to enable financial inclusion in emerging markets</h2><img src="./4.png" width="500">원래 AI/DB쪽으로 들으려다가 해당 강연들이 변경/취소되어 시간이 비어 고민하다가 들어보게 되었다.<p>스마트폰으로 배달, 주차, 학습 등 모든 것을 할 수 있는 시대가 되었으며, 은행업무도 디지털화가 많이 가속되었다며 2025년에는 3.7 trilion만큼의 시장 규모를 예측하고 있다는 내용이었다.</p><p>브라질의 76%, 페루의 63%, 멕시코의 71%, 칠레의 81%들이 디지털뱅크를 사용하고 있다는 연구결과도 보고, 이쪽 분야 초심자로서 내용을 쉽게 풀이해줘서 재밌게 들었다.</p><p>여기는 QnA가 재밌었는데, 인상이 깊어서 한 번 남겨본다.</p><ul><li>Q) 이러한 정보는 어떻게 찾았는가?<br>A) MasterCard, Visa 등에서 연구를 활용한 것이며, 정부에서도 해당 데이터를 종합해서 앞으로의 시장이 어떻게 변화할 것인지 예측하고 있음. 스터디도 찾아보면 많음</li><li>Q) 아메리카에서 최근 대두되고 있는 전략은?<br>A) Cash Free, Bank Transfer를 해결하는 방향으로 전략을 잡고 있다</li><li>Q) 앞으로 디지털뱅크쪽으로 산업이 확장될 것 같은가?<br>A) 전형적인 종이통장은 송금하는데 불편한 점이 많으며, 디지털뱅크는 이 단점을 전부 해결하기 때문에 결국에는 시장을 다 먹을 거긴 할거다. 그게 언젠지는 모르겠지만</li></ul><h2 id="2-WORKSHOP-Unlocking-Growth-AI-Driven-Strategies-Beyond-Metrics-and-Manuals"><a href="#2-WORKSHOP-Unlocking-Growth-AI-Driven-Strategies-Beyond-Metrics-and-Manuals" class="headerlink" title="2. WORKSHOP: Unlocking Growth: AI-Driven Strategies Beyond Metrics and Manuals"></a>2. WORKSHOP: Unlocking Growth: AI-Driven Strategies Beyond Metrics and Manuals</h2><img src="./6.png" width="500">이 분은 교수님인데, AI에 대해서 좀 더 집중적으로 세션을 진행했다.<p>최근 암호화 작업부터 전부 컴퓨터에 의존하고 있는 상황이기 때문에, AI가 결국에는 모든 일을 다 할거다는 얘기와 최근에는 자동화 쪽으로 크게 관심을 받고 있는 상황이라 이 시장을 유의하라고 했다.<br>또한 최근 AI + 오픈소스의 발전으로 개발이 쉬워졌기 때문에 비전공자라도 한 번 개발에 도전을 해볼만하다는 내용이 있었다.</p><p>여기서는 이 내용을 크게 강조했다.</p><ul><li>Identify your key workflows</li><li>Experiment with different ai tools</li><li>Create replicable prompts</li><li>Measure and iterate</li></ul><h2 id="3-PANEL-SESSION-Exploring-the-Power-of-Artificial-Intelligence"><a href="#3-PANEL-SESSION-Exploring-the-Power-of-Artificial-Intelligence" class="headerlink" title="3. PANEL SESSION: Exploring the Power of Artificial Intelligence"></a>3. PANEL SESSION: Exploring the Power of Artificial Intelligence</h2><img src="./3.png" width="500"><p>여기서도 AI가 주제인 만큼, 최근 AI업계의 핫이슈 ChatGPT에 대해 길게 토론하는 시간을 가졌다.<br>내 생각과 동일하게 “간단한 것에 대해서는 사용이 용이하나, C레벨의 임원직인 경우에는 의사결정이 필요할 때 사용을 주의해야 한다”는 내용이었다.</p><p>두 번째로는 AI 학습에 관해서도 얘기를 나눴는데, 인공지능 개발에 있어서도 결국 데이터의 organization이 중요하다며 체계적으로 분류하는 방법에 대해서 연구해야한다고 대화를 나눴다.<br>그리고 데이터를 체계적으로 나누는 것은 결국 자동화가 들어가게 될텐데, 개발자든 비개발자든 이쪽으로도 관심을 가지면 좋을 것이라고 이야기가 와갔다.</p><p>마지막으로 AI개발자의 동향에 관한 토론도 이루어졌는데, 임원직들에게는 “AI개발자를 바로 채용하지 말고 ChatGPT나 Llama3같은 걸 먼저 사용해볼 것. BM 스케일이 커질 때 고용을 고려하라”,<br>개발자에게는 “채용 시장이 동결된 만큼 보수적으로 접근해야 한다. 따라서 AI개발자가 되려면 degree를 못해도 석사까지는 해놓는게 좋을 것 같다”라는 내용으로 정리할 수 있을 것 같다.</p><h2 id="4-PANEL-SESSION-Data-Driven-Innovation"><a href="#4-PANEL-SESSION-Data-Driven-Innovation" class="headerlink" title="4. PANEL SESSION: Data-Driven Innovation"></a>4. PANEL SESSION: Data-Driven Innovation</h2><img src="./5.png" width="500"><p>여기서는 데이터에 관해서 좀 더 자세하게 패널들이 대화를 나누는 세션이었다.</p><p>최근의 데이터는 고객을 이해시키기 위해 사용되는 것이며, “보안, 정확성”에 대한 어필이 좀 더 필요하고<br>기술을 발달로 인해 프레임워크와의 결합이 쉽고 데이터엔지니어들이 파이프라인을 쉽게 구축할 수 있어 무조건적으로 데이터퀄리티를 증가시켜야 한다는 내용이었다.</p><p>다만 체계적으로 정리하는 습관을 기를거면 수학 공부는 무조건적으로 추천하며, AI에 관심이 있다면 언어 모델 플랫폼이 2년 사이에 많이 발전했기 때문에 퀄리티는 툴마다 다르겠지만 개발은 쉬울 것이라는 얘기가 나왔다.</p><p>결론적으로는 데이터에 대한 “보안, 정확성”에 대해서 정부 규제까지 강화될 것으로 보이기 때문에<br>데이터 모델 리펙토링은 매우 조심해야 한다는 내용이 있었다.</p><p>오픈소스를 기업에서도 사용할텐데, 이 점에서는 오픈소스 개발자들도 유의해야겠는 생각이 들었다.</p><h2 id="1일차-후기"><a href="#1일차-후기" class="headerlink" title="1일차 후기"></a>1일차 후기</h2><ul><li><p>일단 개발자 중심의 컨퍼런스가 아니기 때문에, 컨퍼런스 내용은 일반인들도 이해하기 쉽게끔 구성이 되어 있었다.<br>근데 실리콘밸리 특성답게 결국은 개발자들끼리 자발적으로 모여서 추가로 의견을 나누는 시간을 가질 수 있어서 컨퍼런스가 재미있었던 것 같다.<br>다들 따스웠던게, 요즘 트랜드 파악 겸 오픈소스 개발하는데 좀 도움되는 정보 있나 싶어 한국에서 비행기타고 와서 참가했다고 얘기하니까<br>비행기 타고 오느라 힘들었겠다며 음식을 이것 저것 잘 챙겨줬다. 영어도 잘한다고 칭찬해주고.. <del>토종한국인은 감동의 눈물을 흘렸어요</del></p><p>아래는 대화 내용을 요약한 것이다.</p><ul><li>오픈소스에 대해서는 다른 나라에서도 생태계를 키우려고 노력한다고 한다. 듣기로는 프랑스(였던걸로 기억하는데 정확하지는 않고.. 일단 유럽 어딘가)는 올해의 오픈소스를 매년 선정하여 해당 메인 컨트리뷰터에게 상금을 준다고 한다. 외국인도 돈을 준다고 하던데 한 번 관심 있으면 찾아보라고 하더라</li><li>최근 이슈였던 Redis에 대해서도 열띤 토론을 했는데, 다들 이번 사태로 오픈소스 생태계가 어느 정도 수축되었다며 아쉬어하는 목소리를 냈다.</li><li>당연히 AI와 데이터베이스에 대해서도 대화를 나눴다. 여기서도 동일하게 AI든 데이터베이스든 일단 organization이 제대로 되야 관리면에서, 학습면에서 모두 좋은 결과물을 얻을 수 있다고 얘기했다.<br>그 외 평소에 궁금했던 것들(ex. 데이터베이스에 로컬시간 저장 관련)을 물어봤는데, 다들 사람인지라 의견이 다들 다르더라. 근데 얘기를 들어보면 정리 스타일만 다를 뿐 데이터를 체계적으로 정리한 것은 동일했기에 모두 들어볼 만 했다.<br>그것보다 대화를 나누는데 서로 의견을 존중하면서 의견 어필하는게 인상 깊었다. 여기는 말하는 것부터도 되게 전문적으로 하는 느낌이랄까.</li><li>취직 면에서는… 미국 시장은 아직도 매우 차갑다고 한다.<br>실제로 나를 마음에 들어했던 빅테크 기업 개발자는 시장이 어느정도 괜찮으면 나보고 여기서 일하자고 추천하고 싶은데, 여기가 너무 살얼음판이다보니 지금 직장에 계속 다니는게 안전해 보인다며 아쉬워하더라. (실리콘밸리 자체가 쉽게 취직되고 잘리는 곳이라지만, 요즘 정리해고는 쉽고 취직/이직이 어려워졌다고 한숨을 쉬는 건 덤)<br>개인적인 생각이다만 미국이 한국 시장의 1년 뒤라고 생각하고 보는 편인데, 아직도 여기가 얼음장인거 보면 취직 시장이 참 춥구나 싶은 생각이 든다 ㅠㅠ</li></ul></li></ul><ul><li><p>원래 이날 듣고 싶었던 컨퍼런스 주제가</p><ul><li>AI translation & language accessibility</li><li>Business Models Innovation using AI Technology Workshop</li></ul><p>였으나, 아쉽게도 해당 주제는 변경/취소되어 참석하지 못했다. 특히 NLP쪽으로 관심이 많았는데 좀 많이 아쉽더라 🥲</p></li><li><p>왜인지 모르겠으나 앞에 빈자리가 많음에도 불구하고 뒤에서 서서 듣는 사람들이 많았다. 중간에 나갈려고 그런가 싶었는데, 그건 또 아닌 것 같았다. 대부분 들어와서 끝까지 듣더라. 여기 문화가 그런건지, 아니면 앞에 앉아서 주목받기 싫어서 그런건지는 아직도 미스테리 ㅎㅎ;;</p></li></ul>]]></content>
<categories>
<category> ✈️ Conference </category>
</categories>
<tags>
<tag> Conference </tag>
</tags>
</entry>
<entry>
<title>[TIPS] Python, Javascript 프로젝트에서 유용한 regex 문법 및 관련 패키지 모음집</title>
<link href="/2022/11/25/Tips/Regex/"/>
<url>/2022/11/25/Tips/Regex/</url>
<content type="html"><![CDATA[<p>사용자의 입력값이 유효한지 확인하는 작업을 해야할 때가 있는데, 이 때 사용하면 유용한 패키지와 각종 regex를 정리해봤습니다.</p><p>(상시 업데이트 예정)</p><h2 id="Python3"><a href="#Python3" class="headerlink" title="Python3"></a>Python3</h2><hr><ul><li><a href="https://pydantic-docs.helpmanual.io/">pydantic</a></li></ul><br><h3 id="Email-Validation"><a href="#Email-Validation" class="headerlink" title="- Email Validation"></a>- Email Validation</h3><ul><li><p>RFC-5322 표준 기준</p> <pre class="language-python" data-language="python"><code class="language-python"><span class="token keyword">import</span> reEMAIL_REGEX <span class="token operator">=</span> <span class="token string">r"((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))g"</span><span class="token keyword">return</span> re<span class="token punctuation">.</span>fullmatch<span class="token punctuation">(</span>EMAIL_REGEX<span class="token punctuation">,</span> email<span class="token punctuation">)</span> <span class="token comment"># True or False</span></code></pre></li></ul><h3 id="Name-Validation"><a href="#Name-Validation" class="headerlink" title="- Name Validation"></a>- Name Validation</h3><ul><li>letter(a-z, A-Z), apostrophe, hyphen 지원</li><li>여기서는 최소 길이 1 및 최대 길이 20인 문자열만 유효한 것으로 판단 <pre class="language-python" data-language="python"><code class="language-python"><span class="token keyword">import</span> reNAME_REGEX <span class="token operator">=</span> <span class="token string">r"(^[aA-zZ '-]{1,20})"</span><span class="token keyword">return</span> re<span class="token punctuation">.</span>fullmatch<span class="token punctuation">(</span>NAME_REGEX<span class="token punctuation">,</span> name<span class="token punctuation">)</span> <span class="token comment"># True or False</span></code></pre></li></ul><p><br><br><br></p><h2 id="Javascript"><a href="#Javascript" class="headerlink" title="Javascript"></a>Javascript</h2><hr><ul><li><a href="https://github.com/jquense/yup">yup</a>: 주로 <a href="https://formik.org/">formik</a>과 합쳐서 사용한다</li></ul><br><h3 id="Email-Validation-1"><a href="#Email-Validation-1" class="headerlink" title="- Email Validation"></a>- Email Validation</h3><ul><li>RFC-5322 표준 기준 <pre class="language-javascript" data-language="javascript"><code class="language-javascript"><span class="token keyword">const</span> <span class="token constant">EMAIL_REGEX</span> <span class="token operator">=</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">^[-a-z0-9!#$%&'*+/=?^_`{|}~]+(\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$</span><span class="token regex-delimiter">/</span><span class="token regex-flags">i</span></span><span class="token punctuation">;</span><span class="token keyword">const</span> ex1 <span class="token operator">=</span> <span class="token string">"I can do it"</span><span class="token punctuation">;</span><span class="token keyword">const</span> ex2 <span class="token operator">=</span> <span class="token string">"my.email@address.com"</span><span class="token punctuation">;</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>ex1<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token constant">EMAIL_REGEX</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>ex2<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token constant">EMAIL_REGEX</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// true</span></code></pre></li></ul>]]></content>
<categories>
<category> 💾 Etc </category>
</categories>
<tags>
<tag> Python3 </tag>
<tag> Regex </tag>
<tag> Javascript </tag>
</tags>
</entry>
<entry>
<title>네트워크 기본</title>
<link href="/2022/05/30/NS/Basic/"/>
<url>/2022/05/30/NS/Basic/</url>
<content type="html"><![CDATA[<h2 id="IPv4"><a href="#IPv4" class="headerlink" title="IPv4"></a>IPv4</h2><hr><ul><li>컴퓨터 사이에 통신을 하기 위해서는 컴퓨터 위치값을 알아야 함</li><li>이때 각 컴퓨터의 <code>위치값(주소)</code> IP 주소라고 지칭함 (IP Version 4)</li><li>점<code>.</code> 사이의 숫자들은 옥텟(octet)이라고 부름</li></ul><h4 id="IPv4-클래스"><a href="#IPv4-클래스" class="headerlink" title="IPv4 클래스"></a>IPv4 클래스</h4><ul><li>첫 번째 옥탯의 앞자리에 따라 클래스 구분</li><li>주로 A, B class 사용<ul><li><strong>A Class</strong>: 첫 번째 옥탯의 맨 앞자리 비트가 0<ul><li>8bit의 네트워크 bit + 24bit의 호스트 bit</li><li>즉 1개의 네트워크가 2^24개의 ip를 보유</li><li>이러한 네트워크가 2^7개만큼 있음 (식별자 제외)</li><li>대규모 프로젝트에 적합: 국가 단위</li></ul></li><li><strong>B Class</strong>: 첫 번째 옥탯의 앞 두자리의 비트가 1, 0<ul><li>16bit의 네트워크 bit + 16bit의 호스트 bit</li><li>즉 1개의 네트워크가 2^16개의 ip를 보유</li><li>이러한 네트워크가 2^14개만큼 있음 (식별자 제외)</li></ul></li><li><strong>C Class</strong>: 첫 번째 옥탯의 앞 세자리의 비트가 1, 1, 0<ul><li>24bit의 네트워크 bit + 8bit의 호스트 bit</li><li>즉 1개의 네트워크가 2^8개의 ip를 보유</li><li>이러한 네트워크가 2^21개만큼 있음 (식별자 제외)</li><li>소규모 프로젝트에 적합</li></ul></li></ul></li></ul><p><br><br></p><h2 id="Subnet-Sub-Network"><a href="#Subnet-Sub-Network" class="headerlink" title="Subnet: Sub + Network"></a>Subnet: Sub + Network</h2><hr><ul><li>한 사람이 하나의 네트워크를 소유 시 IP가 모자름 -> 모자른 IP를 방지하기 위해 네트워크를 분할하는 개념</li><li>빨간색 부분의 숫자만 바뀌는 것을 확인할 수 있음</li><li>이를 간편하게 표기하기<ul><li><code>[그룹에서 맨 앞의 ip주소/32bit 중 그대로인 부분 개수]</code>로 표기</li><li>subnet A: 211.11.124.0/25</li><li>subnet B: 211.11.124.128/25</li></ul></li></ul><p><br><br></p>]]></content>
<categories>
<category> 🔐 Network/Security </category>
</categories>
<tags>
<tag> Network </tag>
<tag> IP </tag>
<tag> Subnet </tag>
</tags>
</entry>
<entry>
<title>Windows & Mac에서 nvm 설치하기 (Node.js, Npm, Yarn 설치)</title>
<link href="/2022/05/17/Etc/Windows-Mac-Osx-Nvm-Install/"/>
<url>/2022/05/17/Etc/Windows-Mac-Osx-Nvm-Install/</url>
<content type="html"><![CDATA[<p>개발 프로젝트에 따라서 Node의 버전을 여러 개 설치하고 번갈아 사용해야 하는 경우가 있는데,<br>이럴 때는 nvm(Node Version Manager)를 사용하면 여러 개의 node 버전을 설치하여 원하는 node 버전을 골라서 사용할 수 있다.</p><br><h2 id="설치-시-사전-주의-사항"><a href="#설치-시-사전-주의-사항" class="headerlink" title="설치 시 사전 주의 사항"></a>설치 시 사전 주의 사항</h2><ul><li>기존에 Node가 설치되어 있다면 Node를 제거해야 한다.</li><li>글은 업로드한 날짜 그대로 있지만, node는 프로그램으로 계속 끊임없이 업데이트되며 발전한다.<br>글에 나와있는 버전 그대로 따라 치는 것보다는 <strong>자신이 사용할 버전을 직접 고르고 설치할 것</strong>을 추천한다.<br>설치할 수 있는 node 버전 리스트는 여기서 참고하면 된다: <a href="https://nodejs.org/ko/download/releases/">https://nodejs.org/ko/download/releases/</a></li></ul><p><br><br></p><h2 id="Windows에서-설치하기"><a href="#Windows에서-설치하기" class="headerlink" title="Windows에서 설치하기"></a>Windows에서 설치하기</h2><ol><li><a href="https://github.com/coreybutler/nvm-windows/releases">nvm-windows repository</a>에 접속하여 release된 파일을 다운받는다. 이 때 파일유형이 <code>.zip</code>인 것을 다운받는다.<img src="/2022/05/17/Etc/Windows-Mac-Osx-Nvm-Install/1.png" class="" title="nvm-windows repository 다운로드"></li></ol><br><ol start="2"><li>압축을 풀면 폴더 내부에 <code>nvm-setup.exe</code> 파일이 있는데, 해당 파일을 실행하면 nvm이 설치된다.<img src="/2022/05/17/Etc/Windows-Mac-Osx-Nvm-Install/2.png" class="" title="nvm-setup.exe 파일"></li></ol><br><ol start="3"><li>윈도우 터미널에서 아래 명령어들을 통해 node를 버전별로 설치 및 관리, 사용을 할 수 있다.<pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># nvm 버전 확인</span>nvm version<span class="token comment"># 설치된 node 리스트</span>nvm <span class="token function">ls</span><span class="token comment"># node 버전별 설치하기</span>nvm <span class="token function">install</span> <span class="token number">14.17</span>.6<span class="token comment"># nvm에서 특정 node 버전 활성화하기</span>nvm use <span class="token number">15.11</span>.0<span class="token comment"># npm으로 yarn 설치하기</span><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> <span class="token function">yarn</span></code></pre></li></ol><p><br><br></p><h2 id="Mac에서-설치하기"><a href="#Mac에서-설치하기" class="headerlink" title="Mac에서 설치하기"></a>Mac에서 설치하기</h2><ul><li><p>nvm repository: <a href="https://github.com/nvm-sh/nvm">https://github.com/nvm-sh/nvm</a></p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># nvm 설치하기</span><span class="token function">curl</span> -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh <span class="token operator">|</span> <span class="token function">bash</span><span class="token comment"># node 버전별 설치하기</span>nvm <span class="token function">install</span> <span class="token number">14.17</span>.3<span class="token comment"># nvm에서 특정 node 버전 활성화하기</span>nvm use <span class="token number">15.11</span>.0<span class="token comment"># npm으로 yarn 설치하기</span><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> <span class="token function">yarn</span></code></pre></li><li><p>만약에 nvm을 설치하고 확인했을 때 <code>command not found</code>가 나오면 <code>bash_profile</code>을 확인하자.</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">vi</span> ~/.bash_profile<span class="token comment"># 아래의 코드 확인. 오타나 누락 시 직접 수정</span><span class="token builtin class-name">export</span> <span class="token assign-left variable">NVM_DIR</span><span class="token operator">=</span><span class="token string">"<span class="token environment constant">$HOME</span>/.nvm"</span><span class="token punctuation">[</span> <span class="token parameter variable">-s</span> <span class="token string">"<span class="token variable">$NVM_DIR</span>/nvm.sh"</span> <span class="token punctuation">]</span> <span class="token operator">&&</span> <span class="token builtin class-name">.</span> <span class="token string">"<span class="token variable">$NVM_DIR</span>/nvm.sh"</span> <span class="token comment"># This loads nvm</span><span class="token comment"># 재시작</span><span class="token builtin class-name">source</span> ~/.bash_profile</code></pre></li></ul>]]></content>
<categories>
<category> 💾 Etc </category>
</categories>
<tags>
<tag> Node.js </tag>
<tag> Npm </tag>
<tag> Yarn </tag>
<tag> Nvm </tag>
</tags>
</entry>
<entry>
<title>DevOps 개요</title>
<link href="/2022/05/12/DevOps/Intro/"/>
<url>/2022/05/12/DevOps/Intro/</url>
<content type="html"><![CDATA[<h2 id="1-DevOps-정의"><a href="#1-DevOps-정의" class="headerlink" title="1. DevOps 정의"></a>1. DevOps 정의</h2><hr><blockquote><p>“A set of practices intended to reduce the time between commiting a change to a system and the change being placed into normal production, while ensuring high quality”</p><p>제품의 변경사항을 품질을 보장함과 동시에 프로덕션에 반영하는데 걸리는 시간을 단축하기 위한 실천 방법의 모음</p></blockquote><ul><li>개발(Dev)와 운영(Ops)의 합성어</li><li>개발과 운영의 경계를 허물과 하나의 팀으로 통합하고자 하는 문화 또는 철학</li><li>개발과 운영의 벽을 허물어 더 빨리 자주 배포하는 것</li></ul><p><br><br></p><h2 id="2-DevOps가-필요한-이유"><a href="#2-DevOps가-필요한-이유" class="headerlink" title="2. DevOps가 필요한 이유"></a>2. DevOps가 필요한 이유</h2><hr><ul><li>조직의 규모가 커지면 각 단계 별 전문가로 구성된 기능 조직을 운영할 수 있음</li><li>그만큼 의사소통이 많아지기 때문에 커뮤니케이션의 문제가 발생</li><li>개발자가 SW 생애주기 중 여러 단계에 참여한다면 이러한 문제를 해결할 수 있음</li></ul><ul><li>넷플릭스에서 제안하는 <strong>Full-cycle Developer</strong></li><li>SW 개발 생애주기의 전체에 직접 참여하는 개발자</li></ul>]]></content>
<categories>
<category> ♼ DevOps </category>
</categories>
<tags>
<tag> DevOps </tag>
</tags>
</entry>
<entry>
<title>대학생 때 알았다면 좋았을 SW 팁</title>
<link href="/2022/05/10/Retrospect/Bachelor/"/>
<url>/2022/05/10/Retrospect/Bachelor/</url>
<content type="html"><![CDATA[<p>SW분야를 선택하고 스펙을 쌓으며 가장 고생했던 것 중 하나가 바로 <strong>조언해주는 사람이 없다는 것</strong>이었다.</p><p>어떤 분야든 멘토가 있다면 효율적으로 스펙을 쌓고 빠르게 기술을 습득할 수 있겠지만, 주변에 컴퓨터 분야에 종사하는 사람이 눈 씻고봐도 전혀 없었다.<br>심지어 대학교 자체를 나온 사람이 주변에 친언니(문과 전공) 하나밖에 없었기에, 혼자서 익스트림 칠전팔기 도전기를 찍었던 것 같다.</p><p>나같은 사람이 더 이상 없길 바라며, 나름 생각하는 꿀팁들을 적어보려고 한다.</p><blockquote><p>추가적으로 궁금한 내용 있으시면 언제든지 편하게 댓글 남겨주세요 :-)</p></blockquote><p><br><br></p><h2 id="언어-선택"><a href="#언어-선택" class="headerlink" title="언어 선택"></a>언어 선택</h2><p>본인이 하고 싶은 분야의 언어를 선택한다. 딱히 하고 싶은 분야가 없다면 제일 무난한 <code>Java</code>를 추천한다. 취업시장 자체가 서버 개발자 수요가 많은데, 그 중 전반적으로 가장 많이 구하는 기술 스택이 바로 <code>Java</code>이기 때문이다.<br>다만 스타트업을 노리고 있다면 <code>Javascript</code>, <code>Python</code>을 추천한다. 최근 스타트업들이 이 두 언어를 주요 기술 스택으로 선택하는 추세다.</p><p>코딩 테스트 언어로는 무난하게 <code>C/C++</code>, <code>Python</code>을 추천한다. 다른 언어를 선택해도 관계없다. 자신이 가장 자신있는 언어를 선택하는 것이 좋다.<br>주변에서 종종 시간초과 문제 때문에 <code>Python</code>을 망설이는 경우가 있는데, 시간초과 문제는 나중에 해결할 일이다. 일단 돌아가는 코드를 만들어야 부분점수라도 얻을 수 있지 않겠는가.</p><p>사실 학부생 수준에서 언어 선택은 크게 중요하지 않다. 왜냐면 취직해봤자 온보딩만 평균 1개월-3개월 정도 돌리는데 그러면 일은 커녕 공부만 주구장창 하고있기 때문이다.<br>(카카오 다니는 지인 왈, <strong>입사한지 5개월 됐는데 일 하나도 안하고 공부만 했어…</strong> <del>물경력</del>)<br>그래서 학부생 시절에 될 수 있으면 다양한 수업을 접하면서 여러 언어들을 익혀보는 것을 추천한다.<br>다만 분야마다 주력으로 하는 언어가 있기 때문에, 특정 분야를 가기로 결심했으면 그 언어는 공부해놓는게 좋다.</p><table> <tbody><tr> <td><b>분야</b></td> <td><b>언어</b></td> </tr> <tr> <td>AI/빅데이터</td> <td>Python</td> </tr> <tr> <td>서버</td> <td>Java</td> </tr> <tr> <td>프론트엔드</td> <td>Javascript</td> </tr> <tr> <td>하드웨어/임베디드</td> <td>C, C++</td> </tr></tbody></table><p><br><br></p><h2 id="졸업-전-반드시-들어야-할-전공-과목"><a href="#졸업-전-반드시-들어야-할-전공-과목" class="headerlink" title="졸업 전 반드시 들어야 할 전공 과목"></a>졸업 전 반드시 들어야 할 전공 과목</h2><ul><li>자료구조</li><li>알고리즘</li><li>데이터베이스</li><li>네트워크</li><li>운영체제</li></ul><p>대부분 기업들이 <code>코딩테스트 > 면접</code> 과정을 거치는데, 먼저 코딩테스트는 대부분 자료구조, 알고리즘 문제가 출제된다.<br>면접에서는 데이터베이스, 네트워크, 운영체제에 관련해 질문이 따발총으로 들어오므로 꼭 수강해놓는 것을 추천한다.<br>이 과목들 중 하나라도 공부를 안하고 취뽀에 도전한다면 현실적으로 많이 힘들다.</p><p><br><br></p><h2 id="비전공자-개발자"><a href="#비전공자-개발자" class="headerlink" title="비전공자 개발자"></a>비전공자 개발자</h2><p>컴퓨터공학이 만만한 학문은 아니지만, 분야 특성 상 인터넷에 수많은 지료들이 존재하며 자료 공유가 활발한 편이다.<br>유튜브, 카카오톡 오픈톡방, 그 외 다양한 커뮤니티들 및 사이트에서 원하는 정보를 쉽게 찾을 수 있다.</p><p>그래도 적성이 안맞으면 굉장히 괴로운 분야이기 때문에 대학생이라면 프로그래밍 과목을 한 번 들어보고, 아니라면 인터넷 자료들을 통해 공부를 해보는 것을 추천한다.</p><p><br><br></p><h2 id="학교-비전공자-메리트"><a href="#학교-비전공자-메리트" class="headerlink" title="학교 / 비전공자 메리트"></a>학교 / 비전공자 메리트</h2><p>어떤 곳이든 기본적으로 학벌을 보기에 여기 또한 학벌의 존재를 피해갈 순 없다. 다만 다른 분야에 비하면 개발 자체는 학교 학과를 안 보는 직군으로 평가된다.<br>따라서 학교 네임밸류와 컴퓨터 외 전공 때문에 취업이 안된다고 말하는 사람들은 ‘내가 과연 열심히 살았나?’라며 솔직하게 자기반성의 시간을 가져야 한다. 정말 노력만 한다면 커버가 될 수 있기 때문이다.</p><blockquote><p>그럼에도 불구하고 나는 안된다 하시는 분들, 경쟁자들과 비빌 수 있는 스펙을 만들기 위해 부던히 노력을 하셨습니까? 남들이 놀 때 정말 잠도 안자고 공부하셨나요? 가슴에 손을 얹고 한 번 생각해보시길 바랍니다.</p></blockquote><p>비전공자들에 관련해서 추가적인 조언을 해보자면, 비전공자가 뽑히지 않는 이유는 <code>전공자와 비교했을 때 그만한 경험과 지식이 부족하다고 판단</code>되기 때문이다. 대부분의 비전공자들이 부트캠프나 국비지원으로 개발공부를 하는데, 이 모든 과정들이 길어봤자 1년이기 때문에 현실적으로도 4년제 전공생들과 비교되는건 당연하다.</p><p>그래도 개발 스펙을 쌓고자 한다면 개인적으로 추천하는 것은 <code>부트캠프</code>이다.<br>이미 개발자들 사이에서 <strong>국비지원 = 개발자 찍어대는 공장 -> 국비지원 출신 = 코딩 대충 배운 사람</strong>으로 인식하는 경우가 많기 때문이다. (좆소기업을 유심히 살펴보면 국비지원 출신이 많은 경우를 찾을 수 있다)</p><p>다음은 그 중 추천하는 부트캠프 프로그램이다.</p><ul><li><a href="https://woowacourse.github.io/">우아한 테크코스 (우테코)</a></li><li><a href="https://boostcamp.connect.or.kr/">BoostCamp (부스트캠프 / 부캠)</a></li><li><a href="https://www.swmaestro.org/sw/main/main.do">SW마에스트로(소마)</a></li><li><a href="https://42seoul.kr/seoul42/main/view">42SEOUL</a>: 프랑스의 유명 프로그램인 42Ecole을 벤치마킹한 대한민국 정부 지원 SW 개발자 양성 프로그램이다. 전 세계적으로 유명한 프로그램인 만큼 회사 지원했을 때 도움되는 경우가 많다.</li><li><a href="https://www.ssafy.com/">Ssafy(싸피)</a>: 삼성에서 지원하는 SW 개발자 양성 프로그램이다. 졸예자 이상만 참가 가능하다.</li></ul><p>국비지원을 선택했더라도 자신의 노력에 달렸으니 더더욱 열심히 CS공부를 하는 것을 추천한다.</p><p><br><br></p><h2 id="개발자로-지원할-수-있는-기업"><a href="#개발자로-지원할-수-있는-기업" class="headerlink" title="개발자로 지원할 수 있는 기업"></a>개발자로 지원할 수 있는 기업</h2><p>크게 두 가지로 나뉜다.<br>(금융권 IT 등 다양하게 있겠지만 이 쪽으로는 잘 몰라서 패스)</p><ul><li><strong>대기업 IT 자회사</strong>: LG CNS, 현대오토에버, 삼성 SDS 등<ul><li>그룹 계열사의 IT 서비스를 개발 및 운영</li><li>흔히 대기업 SI로 불리며, 취업 시장에서 가장 많이 채용</li><li>장점: 높은 연봉과 안정성</li><li>단점: 수직적인 기업문화, 개발 기회가 적음(대체로 운영 업무 위주)</li></ul></li><li><strong>IT 서비스 기업</strong>: 네카라쿠배(네이버, 카카오, 라인, 쿠팡, 배달의 민족)<ul><li>개발적인 측면에서 자유를 보장받을 수 있으며 다양한 기회 또한 접할 수 있음</li><li>계속 개발 공부를 하면서 성장하고 싶다면 추천</li><li>장점: 자유로운 기업문화</li><li>단점: 평생 공부, 기업별 연봉차가 매우 큼(대기업 vs 스타트업), 안정성 보장 못함</li></ul></li></ul>]]></content>
<categories>
<category> 🎈 Retrospect </category>
</categories>
<tags>
<tag> Retrospect </tag>
<tag> Job </tag>
<tag> 42SEOUL </tag>
</tags>
</entry>
<entry>
<title>[MSA] Micro Service Architecture: Outer - Service Mesh</title>
<link href="/2022/04/30/Architecture/MSA-outer-service-mesh/"/>
<url>/2022/04/30/Architecture/MSA-outer-service-mesh/</url>
<content type="html"><![CDATA[<p>MSA의 Outer Architecture 중 Service Mesh에 대해 알아보자.</p><p><br><br></p><h2 id="MSA-개념-설명-읽어보기"><a href="#MSA-개념-설명-읽어보기" class="headerlink" title="MSA 개념 설명 읽어보기"></a>MSA 개념 설명 읽어보기</h2><hr><ul><li><a href="/2022/04/26/Architecture/MSA-Intro/" title="[MSA] Micro Service Architecture 개요">[MSA] Micro Service Architecture 개요</a></li></ul><p><br><br></p><h2 id="Service-Mesh"><a href="#Service-Mesh" class="headerlink" title="Service Mesh"></a>Service Mesh</h2><hr><ul><li>마크크로서비스 간의 통신(네트워크)을 제어하는 역할<ul><li>통신 및 네트워크 기능을 비즈니스 로직과 분리한 네트워크 통신 인프라</li><li>모든 서비스의 인프라 layer로 서비스들 간의 통신 처리</li></ul></li><li>service discovery, service routing, load balancing(트래픽 관리) 및 보안 등을 담당함</li></ul><p><br><br></p><h2 id="API-Gateway와의-차이점"><a href="#API-Gateway와의-차이점" class="headerlink" title="API Gateway와의 차이점"></a>API Gateway와의 차이점</h2><hr><p>먼저 <a href="/2022/04/28/Architecture/MSA-outer-gateway/" title="[MSA] Micro Service Architecture: Outer - External Gateway">[MSA] Micro Service Architecture: Outer - External Gateway</a>를 읽고 아래 표를 보는 것을 추천한다.</p><ul><li>최근 MSA에서 다음과 같이 사용:</li><li>API Gateway는 노출되는 부분(External)에 위치하며 내부서비스를 보호 및 제어하는 역할로 사용</li><li>Service Mesh는 내부 서비스(Internal)에 위치하여 서비스를 관리하는 구조로 사용</li></ul> <table> <tbody><tr> <td></td> <td>API Management</td> <td>Service Mesh</td> </tr> <tr> <td><b>적용되는 위치</b></td> <td>마이크로서비스 그룹의 외부 경계에 위치하여 역할 수행</td> <td>경계 내부에서 역할 수행</td> </tr> <tr> <td><b>아키텍쳐 형태</b></td> <td>중앙집중형 아키텍쳐 = SPOF(Single Point of Failure) 생성</td> <td>분산형 아키텍쳐 = SPOF를 생성하지 않고 확장이 용이</td> </tr> <tr> <td><b>패턴</b></td> <td>- Gateway proxy pattern 사용<br>- Consumer(호출자)가 구현 내용을 알 필요없이 Gateway를 호출하는 방법만 알면 Gateway가 알아서 수행해주는 방식</td> <td>- Sidecar proxy pattern 사용<br>- Consumer(호출자)의 코드에 Provider(공급자)의 주소를 찾는 방법, failover와 관련된 코드 등의 내용이 들어가게 설정<br>- 호출자의 코드는 어플리케이션 코드(비즈니스 로직)에 내장되지 x, sidecar 형태로 별개로 관리됨</td> </tr> <tr> <td>라우팅 주체</td> <td>서버</td> <td>요청하는 서비스</td> </tr> <tr> <td>라우팅 구성요소</td> <td>별도의 네티워크를 도입하는 독립적인 API gateway 구성 요소</td> <td>서비스 내 sidecar로 Local network 스택의 일부가 됨</td> </tr> <tr> <td>로드 밸런싱</td> <td>- 단일 엔드포인트를 제공<br>- API Gateway 내 로드밸런싱을 담당하는 구성요소에 요청을 redirection하여 해당 구성 요소가 처리함</td> <td>- Service Registry에서 서비스 목록을 수신함<br>- sidecar에서 로드밸런싱 알고리즘을 통해 수행함</td> </tr> <tr> <td>네트워크</td> <td>외부 인터넷과 내부 서비스 네트워크 사이</td> <td>- 내부 서비스 네트워크 사이 <br> - 응용 프로그램의 네트워크 경계 내에서만 통신이 가능하게 함</td> </tr> <tr> <td>분석</td> <td>API에 대한 사용자 및 공급자에 대한 모든 호출에 대해 수집되고 분석</td> <td>Mesh 내 모든 마이크로서비스 구성요소에 대해 분석</td> </tr></tbody></table><p><br><br></p><h2 id="Service-Mesh의-종류"><a href="#Service-Mesh의-종류" class="headerlink" title="Service Mesh의 종류"></a>Service Mesh의 종류</h2><hr><h4 id="PaaS-Platform-as-a-Service-의-일부로-서비스-코드에-포함되는-유형"><a href="#PaaS-Platform-as-a-Service-의-일부로-서비스-코드에-포함되는-유형" class="headerlink" title="PaaS (Platform as a Service)의 일부로 서비스 코드에 포함되는 유형"></a>PaaS (Platform as a Service)의 일부로 서비스 코드에 포함되는 유형</h4><ul><li>Microsoft Azure Service fabric, lagom, SENECA</li><li>프레임워크 기반의 프로그래밍 모델 -> service mesh를 구현하는데 특화한 코드가 필요함(Mesh-native Code)</li></ul><h4 id="라이브러리로-구현되어-API-호출을-통해-Service-mesh에-결합되는-유형"><a href="#라이브러리로-구현되어-API-호출을-통해-Service-mesh에-결합되는-유형" class="headerlink" title="라이브러리로 구현되어 API 호출을 통해 Service mesh에 결합되는 유형"></a>라이브러리로 구현되어 API 호출을 통해 Service mesh에 결합되는 유형</h4><ul><li>Spring Cloud, Netflix OSS(Ribon/Hystrix/Eureka/Archaius), finagle</li><li>프레임워크 라이브러리를 사용<ul><li>Netflix의 Prana는 sidecar 형태로 동작함</li></ul></li><li>Service mesh를 이해하고 코드를 작성해야 함 (Mesh Aware Code)</li></ul><h4 id="Sidecar-proxy를-이용하여-Service-mesh를-마이크로서비스에-주입하는-유형"><a href="#Sidecar-proxy를-이용하여-Service-mesh를-마이크로서비스에-주입하는-유형" class="headerlink" title="Sidecar proxy를 이용하여 Service mesh를 마이크로서비스에 주입하는 유형"></a>Sidecar proxy를 이용하여 Service mesh를 마이크로서비스에 주입하는 유형</h4><ul><li>Istio/Envoy, Consul, Linkerd</li><li>sidecar proxy 형태로 동작</li><li>service mesh와 무관하게 코드 작성<blockquote><p><b>sidecar pattern</b></p><ul><li>컨테이너 배포방식의 경우 모든 응용 프로그램 컨테이너에 추가로 sidecar 컨테이너가 배포됨</li><li>서비스에 들어오거나 나가는 모든 네트워크 트래픽을 처리</li><li>비즈니스 로직이 포함된 실제 서비스와 sidecar가 병렬로 구성됨 = 서비스 호출에서 proxy를 통해 호출(서비스가 직접 서비스 호출x)</li><li>대규모 마이크로서비스 환경이여도 개발자가 별도의 작업 없이 서비스의 연결, 로깅, 모니터링, 보안, 트래픽 제어를 할 수 있음</li><li>최근 Service Mesh에서 Sidecar pattern 유형을 많이 사용하는 추세</li></ul></blockquote></li></ul><p><br><br></p><h2 id="Service-Mesh의-주요-기능"><a href="#Service-Mesh의-주요-기능" class="headerlink" title="Service Mesh의 주요 기능"></a>Service Mesh의 주요 기능</h2><hr><p>일반적으로 Istio나 consul, Linkerd와 같은 Service Mesh 프레임워크들에서 기능 지원</p><ul><li>Service Discovery</li><li>Load balancing (지연시간 기반 / 대기열 기반)</li><li>Dynamic Request Routing</li><li>Circuit Breaking</li><li>암호화 (TLS)</li><li>보안</li><li>Health check, Retry and Timeout</li><li>Metric 수집</li></ul>]]></content>
<categories>
<category> 🔧 Architecture </category>
</categories>
<tags>
<tag> MSA </tag>
<tag> Architecture </tag>
<tag> API </tag>
<tag> ESB </tag>
<tag> Sidecar </tag>
</tags>
</entry>
<entry>
<title>[MSA] Micro Service Architecture: Outer - External Gateway</title>
<link href="/2022/04/28/Architecture/MSA-outer-gateway/"/>
<url>/2022/04/28/Architecture/MSA-outer-gateway/</url>
<content type="html"><![CDATA[<p>MSA의 Outer Architecture 중 External Gateway에 대해 알아보자.</p><p><br><br></p><h2 id="MSA-개념-설명-읽어보기"><a href="#MSA-개념-설명-읽어보기" class="headerlink" title="MSA 개념 설명 읽어보기"></a>MSA 개념 설명 읽어보기</h2><hr><ul><li><a href="/2022/04/26/Architecture/MSA-Intro/" title="[MSA] Micro Service Architecture 개요">[MSA] Micro Service Architecture 개요</a></li></ul><p><br><br></p><h2 id="External-Gateway"><a href="#External-Gateway" class="headerlink" title="External Gateway"></a>External Gateway</h2><hr><img src="/2022/04/28/Architecture/MSA-outer-gateway/1.png" class="" title="MSA External Gateway 구조"><ul><li>전체 서비스 외부로부터 들어오는 접근을 내부 구조를 드러내지 않고 처리하기 위한 요소</li><li>사용자 인증 (Consumer Identity Provider)과 권한 정책관리(Policy Management)를 수행</li><li><code>API Gateway</code>가 가장 핵심적인 역할</li></ul><p><br><br></p><h2 id="API-Gateway"><a href="#API-Gateway" class="headerlink" title="API Gateway"></a>API Gateway</h2><hr><ul><li>서버 최앞단에 위치하여 모든 API 호출을 받음 (API 서버들의 endpoint 단일화를 해주는 또다른 서버)</li><li>API에 대한 인증과 인가 기능을 가지고 있음: 받은 API 호출을 인증한 후, 적절한 서비스들에 메세지를 전달(routing)</li><li>SOA의 핵심 인프라인 <code>ESB</code>(Enterprise Service Bus)에서 비롯됨<ul><li>EBS는 SOAP/XML 기반의 무거운 기능</li><li>API Gateway는 REST/JSON 기반의 가벼운 기능</li></ul></li></ul><p><br><br></p><h2 id="API-Gateway의-주요-기능"><a href="#API-Gateway의-주요-기능" class="headerlink" title="API Gateway의 주요 기능"></a>API Gateway의 주요 기능</h2><hr><ol><li><strong>인증 및 인가</strong> (Authentication and Authorization)<ul><li>MSA에서 각각의 서비스에 API호출에 대한 인증 및 인가를 한다는 것 = 같은 소스코드를 서비스 인스턴스들마다 심어줘야 함 = 소스의 중복이 심하여 유지 관리가 어렵고, 로깅 모니터링을 관리하는 것도 매우 어려워짐</li><li>따라서 인증서 관리나 인증, SSL, 프로토콜 변환화 같은 기능들을 API Gateway에서 오프로드 -> 각각의 서비스 부담 줄이기 및 서비스 관리, 업그레이드가 용이함<blockquote><p>Authentication (인증) vs Authorization (인가)</p><ul><li>Authentication: 유저가 누구인지 확인하는 절차</li><li>Authorizatoin: 어떠한 유저가 특정 자원에 접근하려 할 때, 그에 대한 접근 권한이 있는지 확인하는 절차</li></ul></blockquote></li></ul></li><li><strong>요청 절차의 단순화</strong><ul><li>API Gateway가 없을 시 클라이언트에 여러 서비스들에 대한 요청을 진행해야 함</li><li>API Gateway는 여러 클라이언트의 요청을 단일 클라이언트의 요청으로 대체 가능하도록 만들어줌</li><li>따라서 클라이언트와 백엔드 간 APU 통신량이 줄어 대기시간이 줄어줄이고 효율성을 높일 수 있음</li></ul></li><li><strong>라우팅 및 로드밸런싱</strong><ul><li>클라이언트로부터 접수된 메시지에 따라, API 호출을 적절한 서비스에 라우팅</li><li>서비스 인스턴스들에 대한 부하분산 가능</li></ul></li><li><strong>서비스 오케스트레이션</strong><ul><li><code>오케스트레이션</code>: 여러 개의 마이크로 서비스를 묶어 새로우 서비스를 만드는 개념</li><li>과도한 오케스트레이션 로직: APU Gateway의 부담을 늘리는 것 = 성능 저하</li></ul></li><li><strong>서비스 디스커버리</strong><ul><li>API Gateway는 각 서비스 호출을 위해 서비스마다 IP주소 및 포트번호를 알고 있어야 함</li><li>lagacy 환경에서는 크게 문제될 점이 없지만, 클라우드 환경에서는 <code>동적</code>으로 배포되기 때문에 서비스 위치를 찾는 것이 어려움<ul><li>이러한 서비스의 위치를 찾는 것을 <code>Service Discory</code></li></ul></li><li>서버 사이드나 클라이언트 사이드를 기준으로 서비스 디스커버리 구현 가능</li></ul></li></ol><p><br><br></p><h2 id="API-Gateway-적용-시-고려사항"><a href="#API-Gateway-적용-시-고려사항" class="headerlink" title="API Gateway 적용 시 고려사항"></a>API Gateway 적용 시 고려사항</h2><hr><ul><li><p>API Gateway 계층이 추가적으로 만들어진다는 의미 = 그 만큼 네트워크 latency 증가</p></li><li><p>API Gateway의 Scale-out 적용이 유연하게 일어나지 않을 경우, API Gateway가 병목지점이 되어 어플리케이션의 성능저하가 일어날 수 있음</p></li><li><p>API Gateway의 가장 큰 단점은 <code>API Gateway를 내부 마이크로서비스와 결합한다는 것</code><br>-> 기존 SOA에서의 EBS(Enterprise Service Bus)에서 발생했던 문제점이 다시 발생할 수 있음</p><blockquote><p>2000년대 후반, 많은 SOA 프로젝트가 실패한 이유로 SOA의 핵심적인 요소 중 하나인 ESB가 꼽히는 경우가 많음.</p><ul><li>당시 EBS 내부 처리 로직을 XML 기반으로 했는데, XML의 파싱은 오버헤드가 큰 작업</li><li>EBS는 가벼운 연산 외에도 과도한 Orchestration 등 무거운 로직을 가짐. 대부분 ESB를 Gateway로의 특성이 아닌 시스템을 통합하기 위한 역할로 많이 구현했음</li></ul></blockquote></li></ul>]]></content>
<categories>
<category> 🔧 Architecture </category>
</categories>
<tags>
<tag> MSA </tag>
<tag> Architecture </tag>
<tag> API </tag>
<tag> ESB </tag>
</tags>
</entry>
<entry>
<title>[MSA] Micro Service Architecture 개요</title>
<link href="/2022/04/26/Architecture/MSA-Intro/"/>