Skip to main content

· 8 min read

쿠버네티스 오브젝트 다뤄보기

매니페스트(manifest) : yaml, json

pod

  1. pod 생성하기
 % kubectl create deployment my-httpd --image=httpd --replicas=1 --port 80
deployment.apps/my-httpd created
% kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
my-httpd 1/1 1 1 59s httpd httpd app=my-httpd
% kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-httpd-599f479b45-6fs28 1/1 Running 0 89s 10.244.0.7 k8s-cluster-control-plane <none> <none>
  1. pod 삭제
% kubectl delete deployment my-httpd
deployment.apps "my-httpd" deleted
  1. pod 속성 수정
% kubectl edit deployment my-httpd
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2025-02-02T06:40:45Z"
generation: 1
labels:
app: my-httpd
name: my-httpd
namespace: default
resourceVersion: "2951"
uid: 1b7d0a49-e1b3-447d-9172-af5b64baeef1
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: my-httpd
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: my-httpd
spec:
containers:
- image: httpd
imagePullPolicy: Always
name: httpd
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 1
conditions:
- lastTransitionTime: "2025-02-02T06:40:47Z"
lastUpdateTime: "2025-02-02T06:40:47Z"
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
- lastTransitionTime: "2025-02-02T06:40:45Z"
lastUpdateTime: "2025-02-02T06:40:47Z"
message: ReplicaSet "my-httpd-599f479b45" has successfully progressed.
reason: NewReplicaSetAvailable
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 1
replicas: 1
updatedReplicas: 1
  1. pod 접속
% kubectl get pod
NAME READY STATUS RESTARTS AGE
my-httpd-599f479b45-bwfc6 1/1 Running 0 93s
cheonkyu@cheonkyuui-Macmini k8s % kubectl exec --it my-httpd-599f479b45-bwfc6 -- /bin/bash
error: unknown flag: --it
See 'kubectl exec --help' for usage.
cheonkyu@cheonkyuui-Macmini k8s % kubectl exec -it my-httpd-599f479b45-bwfc6 -- /bin/bash
root@my-httpd-599f479b45-bwfc6:/usr/local/apache2# ls
bin build cgi-bin conf error htdocs icons include logs modules
root@my-httpd-599f479b45-bwfc6:/usr/local/apache2# ls -al
total 56
drwxr-xr-x 1 www-data www-data 4096 Jan 24 01:28 .
drwxr-xr-x 1 root root 4096 Jan 24 01:25 ..
drwxr-xr-x 2 root root 4096 Jan 24 01:28 bin
drwxr-xr-x 2 root root 4096 Jan 24 01:28 build
drwxr-xr-x 2 root root 4096 Jan 24 01:28 cgi-bin
drwxr-xr-x 4 root root 4096 Jan 24 01:28 conf
drwxr-xr-x 3 root root 4096 Jan 24 01:28 error
drwxr-xr-x 2 root root 4096 Jan 24 01:28 htdocs
drwxr-xr-x 3 root root 4096 Jan 24 01:28 icons
drwxr-xr-x 2 root root 4096 Jan 24 01:28 include
drwxr-xr-x 1 root root 4096 Feb 2 06:40 logs
drwxr-xr-x 2 root root 4096 Jan 24 01:28 modules
  1. pod 로그 보기
% kubectl logs my-httpd-599f479b45-bwfc6
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.244.0.8. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.244.0.8. Set the 'ServerName' directive globally to suppress this message
[Sun Feb 02 06:40:47.320976 2025] [mpm_event:notice] [pid 1:tid 1] AH00489: Apache/2.4.63 (Unix) configured -- resuming normal operations
[Sun Feb 02 06:40:47.321109 2025] [core:notice] [pid 1:tid 1] AH00094: Command line: 'httpd -D FOREGROUND'

deployment(디플로이먼트)

롤링 : 새버전의 애플리케이션은 하나씩 늘리고, 기존 버전의 애플리케이션은 하나씩 줄여나가는 방식 재생성 : 이전 버전의 파드를 모두 종료하고 새 버전 파드를 일괄적으로 교체하는 방식 블루/그린 : 이전 버전과 새 버전이 동시에 운용됨 카나리 : 애플리케이션의 몇몇 새로운 기능을 테스트할 때 사용됨. 두 버전 모두 배포하지만 새 버전에는 트래픽을 조금씩 흘려보내 테스트 테스트 후 이상이 없다고 판단되면 이전 버전을 종료하고 새 버전으로만 서비스

1

쿠버네티스 대시보드 구성

kubectl apply -f service-acount.yaml
kubectl apply -f cluster-role-binding.yaml

https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md

kubectl get secret admin-user -n kubernetes-dashboard -o jsonpath="{.data.token}" | base64 -d
kubectl -n kubernetes-dashboard delete serviceaccount admin-user
kubectl -n kubernetes-dashboard delete clusterrolebinding admin-user

쿠버네티스 자동완성 활성화하기

source <(kubectl completion zsh)

클러스터 상태에 대한 자세한 스냅샷 얻기

kubectl cluster-info dump --all-namespaces --output-directory=$PWD/cluster-state
% tree
.
├── default
│ ├── daemonsets.json
│ ├── deployments.json
│ ├── events.json
│ ├── nginx-deploy-96b9d695-jjvdq
│ │ └── logs.txt
│ ├── nginx-deploy-96b9d695-vskd4
│ │ └── logs.txt
│ ├── pods.json
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
├── kube-node-lease
│ ├── daemonsets.json
│ ├── deployments.json
│ ├── events.json
│ ├── pods.json
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
├── kube-public
│ ├── daemonsets.json
│ ├── deployments.json
│ ├── events.json
│ ├── pods.json
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
├── kube-system
│ ├── coredns-668d6bf9bc-knrw9
│ │ └── logs.txt
│ ├── coredns-668d6bf9bc-qlkds
│ │ └── logs.txt
│ ├── daemonsets.json
│ ├── deployments.json
│ ├── etcd-k8s-cluster-control-plane
│ │ └── logs.txt
│ ├── events.json
│ ├── kindnet-6f9js
│ │ └── logs.txt
│ ├── kube-apiserver-k8s-cluster-control-plane
│ │ └── logs.txt
│ ├── kube-controller-manager-k8s-cluster-control-plane
│ │ └── logs.txt
│ ├── kube-proxy-zwqcg
│ │ └── logs.txt
│ ├── kube-scheduler-k8s-cluster-control-plane
│ │ └── logs.txt
│ ├── pods.json
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
├── kubernetes-dashboard
│ ├── daemonsets.json
│ ├── dashboard-metrics-scraper-5bd45c9dd6-bsklg
│ │ └── logs.txt
│ ├── deployments.json
│ ├── events.json
│ ├── kubernetes-dashboard-7cd5f76ddb-5hkwg
│ │ └── logs.txt
│ ├── pods.json
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
├── local-path-storage
│ ├── daemonsets.json
│ ├── deployments.json
│ ├── events.json
│ ├── local-path-provisioner-58cc7856b6-nxhm4
│ │ └── logs.txt
│ ├── pods.json
│ ├── replicasets.json
│ ├── replication-controllers.json
│ └── services.json
└── nodes.json

쿠버네티스 통신

https://kubernetes.io/ko/docs/concepts/cluster-administration/networking/

  • 파드가 사용하는 네트워크와 호스트(노드)가 사용하는 네트워크는 다르다.
  • 같은 노드에 떠 있는 파드끼리만 통신할 수 있다.
  • 다른 노드의 파드와 통신하려면 CNI 플러그인이 필요하다.

CNI(container network interface)

https://itnext.io/tracing-pod-to-pod-network-traffic-in-kubernetes-112523a325b2

같은 파드에 포함된 컨테이너 통신

파드 당 하나의 IP가 할당됨

파드 내 컨테이너는 localhost에 포트 번호가 다른 것으로 식별

단일 노드에서 파드 간 통신

브릿지에서 ARP로 IP/MAC 주소를 저장

1

2

3

4

5

다수의 노드에서 파드 간 통신

브릿지에서 다른 대역의 IP라도 판단 후 기본 게이트웨이로 전달

1

브릿지 -> veth -> eth 순으로 통신

2

파드와 서비스 간의 통신

CNI (Container Network Interface) 역할

  • Create interfaces.
  • Create veth pairs.
  • Set up the namespace networking.
  • Set up static routes.
  • Configure an ethernet bridge.
  • Assign IP addresses.
  • Create NAT rules.

netfilter는 패킷 필터링을 구성하고, NAT 또는 포트 변환 규칙을 생성하고, 네트워크의 트래픽 흐름을 관리할 수 있는 프레임워크

iptables는 Linux 커널 방화벽의 IP 패킷 필터 규칙을 구성할 수 있는 사용자 공간 유틸리티 프로그램

클리이언트 파드 (파드 A) 클러스터 DNS 서버(coreDNS)에 A 질의를 해서 서비스 주소를 응답 받음

서비스 IP로 요청할 때

1

DNAT, 목적지 IP를 Service1에서 파드B로 변경

2

3

4

SNAT, 요청 IP를 파드B에서 Service1로 변경 (파드B 소스 IP를 원래 서비스의 vIP로)

5

6

쿠버네티스 DNS

% kubectl describe cm -n kube-system coredns
Name: coredns
Namespace: kube-system
Labels: <none>
Annotations: <none>

Data
====
Corefile:
----
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30 {
disable success cluster.local
disable denial cluster.local
}
loop
reload
loadbalance
}


BinaryData
====

Events: <none>

파드가 생성될 때 /etc/resolv.conf 파일에 coreDNS를 가리키는 IP 주소를 네임서버로 등록한다는 것을 확인

% kubectl run -it --rm busybox --image=busybox --restart=Never -- cat /etc/resolv.conf

search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
pod "busybox" deleted

· 3 min read

k8s는 스스로 관리한다.

  • kind 설치하기
  • 쿠버네티스 컴포넌트

쿠버네티스 컴포넌트

1

https://kubernetes.io/ko/docs/concepts/overview/components/

쿠버네티스는 스스로를 관리한다. (pod으로)

kubeadmin 기반 /etc/kubernetes/manifests 디렉토리의 내용을 조회하면 다음 같은 매니페스트들이 있다.

root@k8s-cluster-control-plane:/etc/kubernetes/manifests# ls
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml

etcd.yaml 매니페스트를 보면 단일 컨테이너를 갖는 파드임을 알 수 있다.

root@k8s-cluster-control-plane:/etc/kubernetes/manifests# cat etcd.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/etcd.advertise-client-urls: https://172.20.0.2:2379
creationTimestamp: null
labels:
component: etcd
tier: control-plane
name: etcd
namespace: kube-system
spec:
containers:
- command:
- etcd
- --advertise-client-urls=https://172.20.0.2:2379
- --cert-file=/etc/kubernetes/pki/etcd/server.crt
- --client-cert-auth=true
- --data-dir=/var/lib/etcd
- --experimental-initial-corrupt-check=true
- --experimental-watch-progress-notify-interval=5s
- --initial-advertise-peer-urls=https://172.20.0.2:2380
- --initial-cluster=k8s-cluster-control-plane=https://172.20.0.2:2380
- --key-file=/etc/kubernetes/pki/etcd/server.key
- --listen-client-urls=https://127.0.0.1:2379,https://172.20.0.2:2379
- --listen-metrics-urls=http://127.0.0.1:2381
- --listen-peer-urls=https://172.20.0.2:2380
- --name=k8s-cluster-control-plane
- --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
- --peer-client-cert-auth=true
- --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
- --snapshot-count=10000
- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
image: registry.k8s.io/etcd:3.5.16-0

api서버, 스케줄러, 컨트롤 매니저도 마찬가지로 단일 컨테이너를 갖는 파드이다.

root@k8s-cluster-control-plane:/etc/kubernetes/manifests# cat kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 172.20.0.2:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=172.20.0.2
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --runtime-config=
- --secure-port=6443
- --service-account-issuer=https://kubernetes.default.svc.cluster.local
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/16
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: registry.k8s.io/kube-apiserver:v1.32.0
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --allocate-node-cidrs=true
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=127.0.0.1
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --cluster-cidr=10.244.0.0/16
- --cluster-name=k8s-cluster
- --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
- --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
- --controllers=*,bootstrapsigner,tokencleaner
- --enable-hostpath-provisioner=true
- --kubeconfig=/etc/kubernetes/controller-manager.conf
- --leader-elect=true
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --root-ca-file=/etc/kubernetes/pki/ca.crt
- --service-account-private-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/16
- --use-service-account-credentials=true
image: registry.k8s.io/kube-controller-manager:v1.32.0

root@k8s-cluster-control-plane:/etc/kubernetes/manifests# cat kube-scheduler.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
component: kube-scheduler
tier: control-plane
name: kube-scheduler
namespace: kube-system
spec:
containers:
- command:
- kube-scheduler
- --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
- --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
- --bind-address=127.0.0.1
- --kubeconfig=/etc/kubernetes/scheduler.conf
- --leader-elect=true
image: registry.k8s.io/kube-scheduler:v1.32.0

· 5 min read

애플리케이션 아키텍처 패턴

  • 트랜잭션 스트립트 : 서블릿, JSP, PHP
  • 테이블 모듈 :
  • 서비스 레이어
  • 도메인 모델 패턴

트랜잭션 스크립트 패턴

클라이언트가 요청한 비즈니스 로직을 하나의 프로시저가 모두 처리

테이블 모듈 패턴

데이터베이스 테이블 단위로 비즈니스 로직을 처리하는 클래스를 분리

데이터베이스 테이블에 대응하는 클래스를 테이블 모듈로 선언하고 대상 테이블의 데이터를 조회하거나 변경

서비스 레이어 패턴

도메인 모델 패턴

데이터와 행위를 하나의 객체로 설계하는 패턴

22

비즈니스 로직이 복잡하고 객체지향 언어를 사용하면 도메인 모델 패턴을 우선적으로 고려 엔티티는 식별자를 가지는 반면 값 객체는 식별자를 가지지 않고 엔티티를 수식하는 속성의 그룹

핵사고날 아키텍처

비즈니싀와 기술의 분리를 강조

핵사고날 아키텍처

flowchart LR
애플리케이션_서비스 --> 도메인_객체
애플리케이션_서비스 --> 리포지토리
애플리케이션_서비스 --> 아웃바운드_어뎁터
인바운드_어댑터 --> 애플리케이션_서비스
flowchart LR
인바운드_어댑터 --①--> 애플리케이션_서비스
애플리케이션_서비스 --②--> 리포지토리
리포지토리 --③--> 도메인_객체
애플리케이션_서비스 --④--> 도메인_객체
애플리케이션_서비스 --⑤--> 리포지토리
애플리케이션_서비스 --⑥--> 아웃바운드_어뎁터

① 외부 요청을 애플리케이션 서비스로 요청 전달 ② 리포지토리에 데이터를 조회 ③ 데이터베이스 조회 결과를 도메인 객체로 변환 ④ 도메인 객체에 요청을 전달해 비즈니스 로직을 수행 ⑤ 데이터베이스에 결과 저장 ⑥ 비즈니스 로직 수행 완료를 외부에 전달

도메인 주도 설계에서 서비스

  • 인프라스트럭처 서비스
  • 애플리케이션 서비스
  • 도메인 서비스

인프라스트럭처 서비스

핵사고날 아키텍처에서 정의한 어뎁터

애플리케이션 서비스

트랜잭션 관리, 인프라스트럭처 서비스와 상호 작용을 포함한 비즈니스 유스케이스의 흐름을 조정하는 두 개의 책임을 가짐

  1. 클라이언트 요청부터 응답까지 하나의 트랜잭션으로 처리
  • 에러를 반환하거나 예외를 던지는 오류 처리
  • 로깅, 매트릭, 모니터링 관련 기능
  1. 비즈니스 유스케이스를 수행하는 일련의 흐름 조정

도메인 서비스

엔티티에 부여하기 적합하지 않은 책임을 도메인 서비스에 부여 기술에 의존성이 없는 POJO로 구현해야함

애그리게이트는 도메인의 불변식을 유지하는 단위로 여러 개의 엔티티와 값 객체로 구성 도메인을 더 깊게 이해할수록 추상 클래스와 인터페이스를 활용해 도메인의 핵심을 표현할 수 있음

· One min read
/* css에 삽입하는 방법 */
body {
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none
}
/*일부 영역에서는 드래그를 허용하고 싶다면, body에 드래그 방지 코드를 넣어놓고, 원하는 영역에 아래 코드를 삽입한다.  */
.draggable {
-webkit-user-select:all;
-moz-user-select:all;
-ms-user-select:all;
user-select:all
}
/*텍스트만 드래그를 허용하고 싶을 경우:*/
.draggable {
-webkit-user-select:text;
-moz-user-select:text;
-ms-user-select:text;
user-select:text
}

· 2 min read

시간별 범위 SQL

쿼리

WITH RECURSIVE
dates (date) AS
(SELECT '2025-01-01 00:00:00' AS time
UNION ALL
SELECT DATE_ADD(date, INTERVAL 1 HOUR) AS time
FROM dates
WHERE date < DATE_SUB('2025-01-02 00:00:00', INTERVAL 1 HOUR))
SELECT * FROM dates;

결과

2025-01-01 00:00:00
2025-01-01 01:00:00
2025-01-01 02:00:00
2025-01-01 03:00:00
2025-01-01 04:00:00
2025-01-01 05:00:00
2025-01-01 06:00:00
2025-01-01 07:00:00
2025-01-01 08:00:00
2025-01-01 09:00:00
2025-01-01 10:00:00
2025-01-01 11:00:00
2025-01-01 12:00:00
2025-01-01 13:00:00
2025-01-01 14:00:00
2025-01-01 15:00:00
2025-01-01 16:00:00
2025-01-01 17:00:00
2025-01-01 18:00:00
2025-01-01 19:00:00
2025-01-01 20:00:00
2025-01-01 21:00:00
2025-01-01 22:00:00
2025-01-01 23:00:00

일자별 범위 SQL

쿼리

WITH RECURSIVE dates (date) AS
(SELECT '2025-01-01 00:00:00' AS time
UNION ALL
SELECT DATE_ADD(date, INTERVAL 1 DAY) AS time
FROM dates
WHERE date < DATE_SUB('2025-01-3
1 00:00:00', INTERVAL 1 DAY))
SELECT *
FROM dates;

결과

2025-01-01 00:00:00
2025-01-02 00:00:00
2025-01-03 00:00:00
2025-01-04 00:00:00
2025-01-05 00:00:00
2025-01-06 00:00:00
2025-01-07 00:00:00
2025-01-08 00:00:00
2025-01-09 00:00:00
2025-01-10 00:00:00
2025-01-11 00:00:00
2025-01-12 00:00:00
2025-01-13 00:00:00
2025-01-14 00:00:00
2025-01-15 00:00:00
2025-01-16 00:00:00
2025-01-17 00:00:00
2025-01-18 00:00:00
2025-01-19 00:00:00
2025-01-20 00:00:00
2025-01-21 00:00:00
2025-01-22 00:00:00
2025-01-23 00:00:00
2025-01-24 00:00:00
2025-01-25 00:00:00
2025-01-26 00:00:00
2025-01-27 00:00:00
2025-01-28 00:00:00
2025-01-29 00:00:00
2025-01-30 00:00:00

· One min read
김천규

패스키란 무엇이고 앞으로의 가능성에 대해 설명했습니다.

간단한 예제 프로그램도 같이 시연했습니다.

세미나Link