블로그

인프런 워밍업 클럽 4기 DevOps 미션 5.

Docker와 Containerd 명령 실습 :https://cafe.naver.com/kubeops/137  [root@cicd-server ~]# mkdir test [root@cicd-server ~]# ls anaconda-ks.cfg gradle-7.6.1-bin.zip original-ks.cfg test [root@cicd-server ~]# cd test [root@cicd-server test]# ls [root@cicd-server test]# curl -O https://raw.githubusercontent.com/k8s-1pro/install/main/ground/etc/docker/Dockerfile % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 60 100 60 0 0 155 0 --:--:-- --:--:-- --:--:-- 155 [root@cicd-server test]# curl -O https://raw.githubusercontent.com/k8s-1pro/install/main/ground/etc/docker/hello.js % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 178 100 178 0 0 497 0 --:--:-- --:--:-- --:--:-- 497 [root@cicd-server test]# ls Dockerfile hello.js  도커 허브 username : chano01794### 빌드 [root@cicd-server test]# docker build -t chano01794/hello:1.0.0 . [+] Building 22.1s (7/7) FINISHED docker:default => [internal] load .dockerignore 0.2s => => transferring context: 2B 0.0s => [internal] load build definition from Dockerfile 0.2s => => transferring dockerfile: 157B 0.1s => [internal] load metadata for docker.io/library/node:slim 2.7s => [internal] load build context 0.1s => => transferring context: 275B 0.1s => [1/2] FROM docker.io/library/node:slim@sha256:b30c143a092c7dced8e17ad67a8783c03234d4844ee84c39090c9780491aaf89 17.6s => => resolve docker.io/library/node:slim@sha256:b30c143a092c7dced8e17ad67a8783c03234d4844ee84c39090c9780491aaf89 0.1s => => sha256:26c0b956ed16dae9780bcb6c24f8161b3f8d44339c435691372d8fe1f270d97b 6.57kB / 6.57kB 0.0s => => sha256:dad67da3f26bce15939543965e09c4059533b025f707aad72ed3d3f3a09c66f8 28.23MB / 28.23MB 2.1s => => sha256:b30c143a092c7dced8e17ad67a8783c03234d4844ee84c39090c9780491aaf89 5.20kB / 5.20kB 0.0s => => sha256:1a6a7b2e2e2c80a6973f57aa8b0c6ad67a961ddbc5ef326c448e133f93564ff9 1.93kB / 1.93kB 0.0s => => sha256:ea9602600a53af802a015c3d6bc5298ce2b6dff888a6f93b2861465f710804c2 3.31kB / 3.31kB 0.8s => => sha256:e03b89ff903664199350b286d7985167de4c079a4ed44baefc09cc1d2e0395ca 51.04MB / 51.04MB 3.7s => => sha256:b03d7a90d937df6cc4c0c2e094b722156a78951c3b5cbe9c383f9a735ea3c316 1.71MB / 1.71MB 1.5s => => sha256:748509dbfbee9fa52f4b018cdaefbc837ee275e458fbdd76b96b312b764a27ac 447B / 447B 2.2s => => extracting sha256:dad67da3f26bce15939543965e09c4059533b025f707aad72ed3d3f3a09c66f8 9.0s => => extracting sha256:ea9602600a53af802a015c3d6bc5298ce2b6dff888a6f93b2861465f710804c2 0.0s => => extracting sha256:e03b89ff903664199350b286d7985167de4c079a4ed44baefc09cc1d2e0395ca 3.2s => => extracting sha256:b03d7a90d937df6cc4c0c2e094b722156a78951c3b5cbe9c383f9a735ea3c316 0.3s => => extracting sha256:748509dbfbee9fa52f4b018cdaefbc837ee275e458fbdd76b96b312b764a27ac 0.0s => [2/2] COPY hello.js . 1.0s => exporting to image 0.2s => => exporting layers 0.1s => => writing image sha256:5965305dbb8602727958bac3dd598f0a55551d0e595c002dad0d07b58c1bdb49 0.0s => => naming to docker.io/chano01794/hello:1.0.0 0.0s ### 이미지 리스트 조회 [root@cicd-server test]# docker image list REPOSITORY TAG IMAGE ID CREATED SIZE chano01794/hello 1.0.0 5965305dbb86 21 seconds ago 230MB chano01794/api-tester v1.0.0 529e022f4e15 2 hours ago 490MB ### 태그 변경 [root@cicd-server test]# docker tag chano01794/hello:1.0.0 chano01794/hello:2.0.0 [root@cicd-server test]# docker image list REPOSITORY TAG IMAGE ID CREATED SIZE chano01794/hello 1.0.0 5965305dbb86 45 seconds ago 230MB chano01794/hello 2.0.0 5965305dbb86 45 seconds ago 230MB chano01794/api-tester v1.0.0 529e022f4e15 2 hours ago 490MB ### 로그인 [root@cicd-server test]# docker login -u chano01794 Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded ### 이미지 업로드 [root@cicd-server test]# docker push chano01794/hello:1.0.0 The push refers to repository [docker.io/chano01794/hello] 98ca109d077d: Pushed 8a45580a6679: Mounted from library/node d5b49e0b6f8f: Mounted from library/node eaf814c4be3d: Mounted from library/node 59cd39de7802: Mounted from library/node 7fb72a7d1a8e: Mounted from library/node ### 이미지 삭제 [root@cicd-server test]# docker rmi chano01794/hello:1.0.0 Untagged: chano01794/hello:1.0.0 [root@cicd-server test]# docker rmi chano01794/hello:2.0.0 Untagged: chano01794/hello:2.0.0 Untagged: chano01794/hello@sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332 Deleted: sha256:5965305dbb8602727958bac3dd598f0a55551d0e595c002dad0d07b58c1bdb49 ### 이미지 다운로드 [root@cicd-server test]# docker pull chano01794/hello:1.0.0 1.0.0: Pulling from chano01794/hello dad67da3f26b: Already exists ea9602600a53: Already exists e03b89ff9036: Already exists b03d7a90d937: Already exists 748509dbfbee: Already exists 88b642bb1537: Already exists Digest: sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332 Status: Downloaded newer image for chano01794/hello:1.0.0 docker.io/chano01794/hello:1.0.0 ### 이미지 -> 파일로 변환 [root@cicd-server test]# docker save -o file.tar chano01794/hello:1.0.0 [root@cicd-server test]# ls file.tar file.tar [root@cicd-server test]# ls Dockerfile file.tar hello.js ### 파일 -> 이미지로 변환 [root@cicd-server test]# docker load -i file.tar Loaded image: chano01794/hello:1.0.0 [root@cicd-server test]# docker image list REPOSITORY TAG IMAGE ID CREATED SIZE chano01794/hello 1.0.0 5965305dbb86 3 minutes ago 230MB chano01794/api-tester v1.0.0 529e022f4e15 2 hours ago 490MB ### 정리 [root@cicd-server test]# docker rmi chano01794/hello:1.0.0 Untagged: chano01794/hello:1.0.0 Untagged: chano01794/hello@sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332 Deleted: sha256:5965305dbb8602727958bac3dd598f0a55551d0e595c002dad0d07b58c1bdb49 [root@cicd-server test]# [root@cicd-server test]# docker image list REPOSITORY TAG IMAGE ID CREATED SIZE chano01794/api-tester v1.0.0 529e022f4e15 2 hours ago 490MB  컨테이너디 ### 네임스페이스 조회 [root@k8s-master ~]# ctr ns list NAME LABELS k8s.io ### 특정 네임스페이스 내 이미지 조회 ctr -n k8s.io image list # 너무 많이 나오는데..? ### 다운로드 및 이미지 확인 (이미지는 default라는 네임스페이스에 다운 받아집니다.) [root@k8s-master ~]# ctr images pull docker.io/chano01794/hello:1.0.0 docker.io/chano01794/hello:1.0.0: resolved |++++++++++++++++++++++++++++++++++++++| manifest-sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:88b642bb153727bb4c187997177696815efcd9d0a0fe0b5757bc2f9c6cad1a3f: done |++++++++++++++++++++++++++++++++++++++| config-sha256:5965305dbb8602727958bac3dd598f0a55551d0e595c002dad0d07b58c1bdb49: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:dad67da3f26bce15939543965e09c4059533b025f707aad72ed3d3f3a09c66f8: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:ea9602600a53af802a015c3d6bc5298ce2b6dff888a6f93b2861465f710804c2: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:e03b89ff903664199350b286d7985167de4c079a4ed44baefc09cc1d2e0395ca: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:b03d7a90d937df6cc4c0c2e094b722156a78951c3b5cbe9c383f9a735ea3c316: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:748509dbfbee9fa52f4b018cdaefbc837ee275e458fbdd76b96b312b764a27ac: done |++++++++++++++++++++++++++++++++++++++| elapsed: 8.0 s total: 77.2 M (9.7 MiB/s) unpacking linux/amd64 sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332... done: 7.472278898s [root@k8s-master ~]# ctr ns list NAME LABELS default k8s.io [root@k8s-master ~]# ctr images list REF TYPE DIGEST SIZE PLATFORMS LABELS docker.io/chano01794/hello:1.0.0 application/vnd.docker.distribution.manifest.v2+json sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332 77.2 MiB linux/amd64 - ### 태그 변경 [root@k8s-master ~]# ctr images tag docker.io/chano01794/hello:1.0.0 docker.io/chano01794/hello:2.0.0 docker.io/chano01794/hello:2.0.0 [root@k8s-master ~]# ctr images list REF TYPE DIGEST SIZE PLATFORMS LABELS docker.io/chano01794/hello:1.0.0 application/vnd.docker.distribution.manifest.v2+json sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332 77.2 MiB linux/amd64 - docker.io/chano01794/hello:2.0.0 application/vnd.docker.distribution.manifest.v2+json sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332 77.2 MiB linux/amd64 - ### 업로드 [root@k8s-master ~]# ctr image push docker.io/chano01794/hello:2.0.0 --user chano01794 Password: manifest-sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332: done |++++++++++++++++++++++++++++++++++++++| config-sha256:5965305dbb8602727958bac3dd598f0a55551d0e595c002dad0d07b58c1bdb49: done |++++++++++++++++++++++++++++++++++++++| elapsed: 3.1 s total: 8.9 Ki (2.9 KiB/s) ### 이미지 (namespace : default) -> 파일로 변환 [root@k8s-master ~]# ctr -n default image export file.tar docker.io/chano01794/hello:1.0.0 [root@k8s-master ~]# ls anaconda-ks.cfg file.tar k8s-local-volume monitoring original-ks.cfg ### 파일 -> 이미지로 변환 (namespace : k8s.io) [root@k8s-master ~]# ctr -n k8s.io image import file.tar unpacking docker.io/chano01794/hello:1.0.0 (sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332)...done [root@k8s-master ~]# ctr -n k8s.io image list | grep hello docker.io/chano01794/hello:1.0.0 application/vnd.docker.distribution.manifest.v2+json sha256:7aa9b5ce123a7edc1cbebcd0b85360b3a2b38c8cc71a0ee43036673136858332 77.2 MiB linux/amd64 io.cri-containerd.image=managed ### 삭제 (namespace : k8s.io) [root@k8s-master ~]# ctr -n k8s.io image remove docker.io/chano01794/hello:1.0.0 docker.io/chano01794/hello:1.0.0 [root@k8s-master ~]# ctr -n k8s.io image list | grep hello   Docker 이미지 사이즈 :https://cafe.naver.com/kubeops/158 ### [미션5] Docker vs Containerd 이미지 사이즈 차이 비교 실습 ### [STEP 1] Docker로 이미지 다운로드 및 사이즈 확인 (cicd-server에서 실행) # 목적: Docker가 실제 어떤 이미지 크기를 보여주는지 확인 [root@cicd-server test2]# docker pull 1pro/api-tester:latest latest: Pulling from 1pro/api-tester 38a980f2cc8a: Already exists de849f1cfbe6: Already exists a7203ca35e75: Already exists f3eeefdc54d7: Pull complete 4f4fb700ef54: Pull complete Digest: sha256:189625384d2f2856399f77b6212b6cfc503931e8b325fc1388e23c8a69f3f221 Status: Downloaded newer image for 1pro/api-tester:latest docker.io/1pro/api-tester:latest [root@cicd-server test2]# [root@cicd-server test2]# docker image list REPOSITORY TAG IMAGE ID CREATED SIZE 1pro/api-tester latest 08a0bfca3fbb 19 months ago 490MB # SIZE 약 490MB ### [STEP 2] Containerd로 이미지 다운로드 및 사이즈 확인 (k8s-master에서 실행) # 목적: Containerd는 동일한 이미지를 몇 MB로 보여주는지 확인 [root@k8s-master test2]# ctr image pull docker.io/1pro/api-tester:latest docker.io/1pro/api-tester:latest: resolved |++++++++++++++++++++++++++++++++++++++| index-sha256:189625384d2f2856399f77b6212b6cfc503931e8b325fc1388e23c8a69f3f221: done |++++++++++++++++++++++++++++++++++++++| manifest-sha256:da882e44367891d885b4557726ea8ffa1a6eaa8fe03ce85e1fb55a19dbf55978: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1: done |++++++++++++++++++++++++++++++++++++++| config-sha256:08a0bfca3fbb866e63ea5b601d44877ff76fbe3b054b4e0af06494501d5b0eb3: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:38a980f2cc8accf69c23deae6743d42a87eb34a54f02396f3fcfd7c2d06e2c5b: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:de849f1cfbe60b1c06a1db83a3129ab0ea397c4852b98e3e4300b12ee57ba111: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:a7203ca35e75e068651c9907d659adc721dba823441b78639fde66fc988f042f: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:f3eeefdc54d7d01fa080d4edf5f1ddd080b446a1633ed1b619faff8159f4b083: done |++++++++++++++++++++++++++++++++++++++| elapsed: 3.7 s total: 2.8 Ki (771.0 B/s) unpacking linux/amd64 sha256:189625384d2f2856399f77b6212b6cfc503931e8b325fc1388e23c8a69f3f221... done: 12.67536041s [root@k8s-master test2]# ctr image list | grep api-tester docker.io/1pro/api-tester:latest application/vnd.oci.image.index.v1+json sha256:189625384d2f2856399f77b6212b6cfc503931e8b325fc1388e23c8a69f3f221 248.3 MiB linux/amd64,linux/arm64,unknown/unknown - # SIZE 약 248.3 MiB ### [STEP 3] Docker 이미지 저장 → 파일 (cicd-server에서 실행) # 목적: 동일한 이미지를 파일로 저장했을 때 사이즈 확인 [root@cicd-server test2]# docker save -o docker-image.tar 1pro/api-tester:latest [root@cicd-server test2]# ls -lh docker-image.tar -rw-------. 1 root root 472M Jun 17 16:50 docker-image.tar # -> 약 472MB ### [STEP 4] 파일 전송 → 배포 서버로 (cicd-server에서 실행) [root@cicd-server test2]# scp docker-image.tar root@192.168.56.30:/root root@192.168.56.30's password: docker-image.tar 100% 472MB 34.0MB/s 00:13 ### [STEP 5] Containerd에서 기존 이미지 삭제 후 import (k8s-master에서 실행) # 목적: Docker에서 만든 tar 파일을 Containerd로 import 해보며 사이즈 변화 체크 [root@k8s-master test2]# ctr image rm docker.io/1pro/api-tester:latest docker.io/1pro/api-tester:latest [root@k8s-master test2]# cd .. [root@k8s-master ~]# ls anaconda-ks.cfg docker-image.tar file.tar k8s-local-volume monitoring original-ks.cfg test2 [root@k8s-master ~]# ctr image import docker-image.tar unpacking docker.io/1pro/api-tester:latest (sha256:ce5b6c9e3b93096e8813c99528af58fd2603bb7e46e8023f131acab303e55836)...done [root@k8s-master ~]# ctr image list | grep api-tester docker.io/1pro/api-tester:latest application/vnd.docker.distribution.manifest.v2+json sha256:ce5b6c9e3b93096e8813c99528af58fd2603bb7e46e8023f131acab303e55836 471.5 MiB linux/amd64 - # -> 결과: 약 471.5 MiB # ** scp 로 ~에 보내서 기존에 다른 폴더 만든데랑 꼬여서 중간에 cd.. 넣었... ### [STEP 6] Containerd에서 다시 다운로드 후 파일로 저장 (k8s-master에서 실행) # 목적: Containerd가 다운로드한 이미지를 파일로 export 해보고 크기 비교 [root@k8s-master ~]# ctr image rm docker.io/1pro/api-tester:latest docker.io/1pro/api-tester:latest [root@k8s-master ~]# ctr image pull docker.io/1pro/api-tester:latest docker.io/1pro/api-tester:latest: resolved |++++++++++++++++++++++++++++++++++++++| index-sha256:189625384d2f2856399f77b6212b6cfc503931e8b325fc1388e23c8a69f3f221: done |++++++++++++++++++++++++++++++++++++++| manifest-sha256:da882e44367891d885b4557726ea8ffa1a6eaa8fe03ce85e1fb55a19dbf55978: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1: done |++++++++++++++++++++++++++++++++++++++| config-sha256:08a0bfca3fbb866e63ea5b601d44877ff76fbe3b054b4e0af06494501d5b0eb3: exists |++++++++++++++++++++++++++++++++++++++| layer-sha256:38a980f2cc8accf69c23deae6743d42a87eb34a54f02396f3fcfd7c2d06e2c5b: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:de849f1cfbe60b1c06a1db83a3129ab0ea397c4852b98e3e4300b12ee57ba111: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:a7203ca35e75e068651c9907d659adc721dba823441b78639fde66fc988f042f: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:f3eeefdc54d7d01fa080d4edf5f1ddd080b446a1633ed1b619faff8159f4b083: done |++++++++++++++++++++++++++++++++++++++| elapsed: 3.7 s total: 2.8 Ki (772.0 B/s) unpacking linux/amd64 sha256:189625384d2f2856399f77b6212b6cfc503931e8b325fc1388e23c8a69f3f221... done: 25.828317ms [root@k8s-master ~]# ctr image export containerd-image.tar docker.io/1pro/api-tester:latest [root@k8s-master ~]# ls -lh containerd-image.tar -rw-r--r--. 1 root root 249M Jun 17 17:02 containerd-image.tar # -> 약 249MB ### [STEP 7] 파일 전송 → Docker 서버로 복사 (k8s-master에서 실행) [root@k8s-master ~]# scp containerd-image.tar root@192.168.56.20:/root The authenticity of host '192.168.56.20 (192.168.56.20)' can't be established. ECDSA key fingerprint is SHA256:l/1SJtioqx5vZeMC7uILw/KtalAXEavCzYllT7ZWmuo. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.56.20' (ECDSA) to the list of known hosts. root@192.168.56.20's password: containerd-image.tar 100% 248MB 38.0MB/s 00:06 ### [STEP 8] Docker에서 기존 이미지 삭제 후 containerd 이미지 import (cicd-server에서 실행) [root@cicd-server test2]# ls docker-image.tar [root@cicd-server test2]# docker image rm 1pro/api-tester:latest Untagged: 1pro/api-tester:latest Untagged: 1pro/api-tester@sha256:189625384d2f2856399f77b6212b6cfc503931e8b325fc1388e23c8a69f3f221 Deleted: sha256:08a0bfca3fbb866e63ea5b601d44877ff76fbe3b054b4e0af06494501d5b0eb3 Deleted: sha256:e2fb66ac6dafbd5edcc5d3ff932de5fad633224097fe643b49c04dd9d56193d6 Deleted: sha256:2029a1bf55bf9a369aa169dfc17ecd7da632987259d1c8c303778b24db5f8bbd [root@cicd-server test2]# cd .. [root@cicd-server ~]# ls anaconda-ks.cfg containerd-image.tar gradle-7.6.1-bin.zip original-ks.cfg test test2 [root@cicd-server ~]# docker load -i containerd-image.tar b54d2aff45ec: Loading layer [==================================================>] 17.15MB/17.15MB 5f70bf18a086: Loading layer [==================================================>] 32B/32B Loaded image: 1pro/api-tester:latest [root@cicd-server ~]# docker image list REPOSITORY TAG IMAGE ID CREATED SIZE chano01794/api-tester v1.0.0 529e022f4e15 4 hours ago 490MB 1pro/api-tester latest 08a0bfca3fbb 19 months ago 490MB # -> 결과: 다시 490MB로 증가 

양파쿵야

쿠버네티스 어나더 클래스 지상편: Sprint2 Day15 ArgoCD 1

ArgoCD 아키텍처ArgoCD는 K8s 전용 배포 툴이며 릴리즈 파일 저장소로 반드시 Git을 필요로 한다. 타 시스템⏬Events: 이벤트 버스 구조의 아키텍처 도구⏬Workflow: 워크플로우 매니지먼트 도구 → 받은 이벤트의 조건에 따라 실행 순서를 생성⏬CD (Image Update: 도커 컨테이너 이미지 변경을 감지)⏬Rollouts: 고급 배포 지원 → 특정 배포 전략으로 K8s 자원 생성⏬Kubernetes Git 레파지토리 분리접근 유저별 권한을 관리할 수 있고, 불필요한 코드를 다운로드 받지 않도록 방지한다.App 소스 코드 전용 - 개발자, 소스 빌드App 릴리즈 전용 - 데브옵스 엔지니어 / 개발자Addon 설치 전용 - 운영자 ArgoCD 배포의 필요 정보Application: 하나의 App을 배포하는 단위, Jenkins의 Project JobProject: Application 그룹, Default가 기본값Source: 연동할 Git 정보Destination: 연동할 K8s 클러스터 정보Refresh: 변경 사항 측정 주기Synchronize: K8s 배포 실행 주기GenernalSync Policy: 변경 사항 발생 시 자동 / 수동 배포 지정Sync Option: 배포 상세 옵션 (Namespcae 자동 생성 등)Prune Policy: 리소스 삭제 정책Desired Manifrest: Git에서 다운로드 받은 Manifest, Git에서 수정하고 Refrest 해야한다.Live Manifrest: K8s의 리소스를 조회한 Manifest → diff는 Git에 변경 사행이 반영될 Live와 현재 Live를 비교해 보여준다. ArgoCD 설치 및 배포 (kubectl, helm)  2. App 배포하기 (kubectl) - 22313. App 배포하기 (helm) - 2232Helm의 -f와 동일 

데브옵스 · 인프라워밍업클럽4기

Masocampus

[마소캠퍼스 GEN AI 인사이트] 한컴독스 AI로 살펴보는 스마트한 문서 작업의 세계

우리는 지금, 문서 작성도 AI가 함께하는 시대에 살고 있어요.복잡한 기획서도, 번역이 필요한 보고서도 이젠 AI가 빠르게 처리해주는 시대랍니다!오늘은 한컴독스 AI를 통해 문서 자동화가 어디까지 발전했는지, 그 흐름을 따라가 볼게요 🙂“2024년 9월, 드디어 한컴독스 AI가 정식 출시되었어요!”특이 AI는 단순한 자동 완성 도구가 아니라, 문서 작성부터 요약, 번역, 맞춤법 검사, 이미지 생성까지 척척 해내는 문서 비서예요.키워드만 넣으면 초안이 뚝딱! 정말 놀랍지 않나요?이제 문서는 혼자 만드는 게 아니라 ‘함께’ 만드는 시대예요!여러 사용자가 실시간으로 수정하고 공유하는 기능은 기본, AI 챗봇을 통해 문서 내용도 대화하듯 검색할 수 있어요.한층 스마트해진 협업 환경을 기대해도 좋아요 🙂무려 104종의 템플릿이 한컴독스에 쏙!게다가 Refinder 서비스와 연동되어 사용자가 원하는 정보도 AI가 찾아줘요.직관적이고 편리한 UI/UX 덕분에 누구나 쉽게 사용할 수 있답니다.😲한컴의 AI 전략은 크게 세 가지로 요약돼요.바로 ‘한컴어시스턴트’, ‘한컴피디아’, 그리고 공공·민간기관 대상의 확장성이죠!문서 자동 생성부터 RAG 기반 검색까지, 이제는 진짜 ‘AI가 도와주는 문서 작업’이 현실이 되었어요.한컴은 이제 AI 중심 기업으로 도약 중이에요!AI 에이전트, SaaS 클라우드 서비스까지 적극 추진하며2025년 1분기에는 SaaS 제품 매출 비중이 무려 29%에 도달했답니다.앞으로가 더욱 기대되는 행보예요!한컴독스 AI, 실제로 어떤 점이 좋을까요?✔ 문서 품질 향상✔ 작업 시간 단축✔ 다양한 기기에서 사용 가능✔ 추가 요금 없이 AI 기능 제공✔ 매월 3,000 크레딧 무료생산성 향상에는 이만한 도구가 없겠죠?요즘 AI는 내가 좋아할 콘텐츠를 먼저 추천해줄 정도로 똑똑해졌죠 🙂이제는 텍스트뿐 아니라 이미지, 영상, 심지어 코드까지 직접 만들어주는 시대예요.한컴독스 AI도 그 흐름에 맞춰 초개인화된 문서 작업 경험을 제공하고 있어요!기획서, 보고서 작성할 때 이만한 효율템이 또 있을까요?AI 기능 다 쓰고도 요금 걱정 없는 한컴독스, 진짜 가성비 끝판왕이에요!스마트한 문서 작업, 지금 바로 시작해보세요😊마소캠퍼스는 늘 콘텐츠 제작의 효율을 높이는 도구들을 연구하고 있어요📚효율적이고 스마트한 일의 방식을 통해 성장할 수 있도록 도와드릴게요.🚀 📌 관련 강의 <리더와 실무자를 위한 GPT 전략 & 실행 가이드: AI 비즈니스 실전 팩>AI를 잘 쓰는 순간, 팀의 생산성과 당신의 존재감이 달라집니다.

AI에이전트 개발한컴독스한컴ai문서aiai업무자동화한컴어시스턴트ai기술ai협업스마트워크마소캠퍼스문서자동화

[인프런 워밍업 클럽] 3주차 회고

Pm은 데이터를 활용해서 어떤 일을 하는가?데이터를 비즈니스에 활용한다는 것은 기록을 통해 현황, 패턴을 찾고 미래를 예측한다는 것높은 수준의 수학, 통계학적 지식이 필요하지 않고 사칙연산만 잘하면 된다!PM의 데이터 활용지표를 보고 사업현황 파악하기데이터를 깊이 보고 인사이트 찾기실행 전 타당성 평가하고 우선순위찾기실행 후 정량적으로 측정 및 평가상관관계 찾기필요 역량데이터 축적 역량의도를 갖고 데이터를 정의하기유저행동로그, event taxonomy 설계데이터 활용 역량지표이해하기, 데이터 분석 역량, 툴 사용 역량데이터 역량을 쌓고 싶다면?그렇게 일하는 회사를 가는 것이 좋다지표란?지표의 스펙트럼구체적이고 직접적인 ↔ 간접적인 지표구체적 : 지난 7일 간 회원가입자 수간접적 : 지난 7일간 활성화, 인게이지된 유저proxy 가능한 수준에서 정의하자중요한 것은 팀이 집중해서 성과를 개선하는 것지표 설정 프레임워크프로덕트에 맞는 지표 설정하기특별한 방법은 없으나 제품과 사업에 대한 깊은 고민, 이해가 필요프로덕트와 비즈니스의 본질유저가 오랫동안 이용하게 만드는 서비스 or 유저의 수요를 빠르게 매칭시켜주는 서비스?프로덕트가 잘 되고 있는지 알 수 있는 방법은?지표 설정 시 처음부터 맞는 지표를 찾을 수 없기 때문에 공통 프레임 워크를 통해 지표를 설정함acquisition(획득)충분히 많은 신규 유저/고객을 획득하고 있는가?비용 효율적으로 신규 유저/고객을 획득하고 있는가?CAC(고객 획득 비용) : 고객 1명을 획득하는데 드는 비용Customer Lifetime Value(고객 생애 가치) : CLV,CLTV,LTVpayback perido(투자 회수 기간)activation신규 사용자들이 습관을 형성할 수 있도록 초기 사용자 경험을 설계해야한다.retention 주요 인풋 중 activation이 가장 임팩트가 큼activation 3단계 : setup / aha / habit momenthabit : 사용자가 제품의 핵심 가치를 경험하는 습관을 형성한 순간장기리텐션과의 상관관계를 기반으로 정의 필요aha moment : 사용자가 처음으로 프로덕트의 핵심 가치를 경험하는 순간setup moment : 사용자가 제품의 핵심 가치를 경험하기 위한 준비를 마친 순간ahamoment 도달 전 거쳐야하는 과정신규유저 / setup, aha, habit monet 전환율을 측정engagement리텐션에 영향을 주기 때문에 중요하다.breadth : 얼마나 많은 유저들이 서비스를 이용하는지day, wau, mau로그인, 접속이 아닌 핵심 행위를 했는가를 기준으로 판단해야함depth : 기능을 얼마나 깊이있게 이용하는지액티브 유저 중 n가지 이상의 기능을 이용한 사용자 수, 비율 등시간을 기준으로 : time spent / dau - 10만분/1만명 → 10분frequency : 얼마나 자주 이용하는지dau/mau, dau/wau : 이용 빈도 평균lnessL5+/7 : 1주일 중 5일 혹은 그 이상 제품을 이용한 이용자 수efficiencty : 성공적으로 과업을 완수하는지제품마다 유저들이 수행하는 과업이 다름retention고객이 제품을 계속해서 이용하는 것사업 모델에 따라 기준이 달라질 수 있음고객이 우리가 원하는 행동을 하는지 가 중요함리텐션율 : 특정 기간 동안 고객들이 유지되는 비율제품 이용 주기에 따라 적절한 시간 단위 정해야함측정 기간 단위 : 일 주 월 분기 연 4주 등정의기준 기간분모분자종류코호트 리텐션리텐션 커브Day N retentionBracket(bounded) retentionUnbounded retentionMonetization매출, 기간별 매출 성장율Paying users 수인당 매출(객단가) 지표ARPUARPPUNet revenue retentionMetric Hierarchy지표의 위계구조리텐션 아웃지표는 액티베이션, 인게이지먼트 등 인풋지표에 영향을 받음수식으로 명확히 표현되지 않는 인풋 아웃풋 관계도 중요함eg : 이커머스 - 상품 카테고리 개수, 카테고리 상품 다양성, 얼마나 쉽고 빠르게 찾을 수 있는가 등등프로덕트 비즈니스에 대한 고민다른 프로덕트 케이스 참고 이번주 회고과제 : 시간이 부족해서 급하게 하다보니 충분히 고민할 시간이 부족했다.. 다음 주에는 시간을 잘 확보하자종합첫 과제 회고가 있었다! 수정할 부분을 자체적으로 고쳐서 보완해보자데이터야놀자 행사를 다녀오느라 주말하루를 통으로 날려버리니 시간이 부족했다. 시간 관리를 잘 해야겠다.

기획 · PM· PO

Hino

워밍업 클럽 4기 DevOps - 2주차 발자국

# 발자국인프런 워밍업 클럽 4기 DevOps의 2주차를 완료하고 작성하는 발자국입니다!강의명 : 쿠버네티스 어나더 클래스 (지상편) - Sprint 1, 2# 2주차 회고한 주가 지나서 2주차가 되었습니다!솔직하게..  Probe까지는 이해했습니다. 거기까진 제 머리가 이해할 수 있는 영역이더라고요?근데.. 이후 내용은 들었을 때 전혀 이해하지 못했습니다! (큰일났음을 감지) 하.. 그래서 매일 반복해서 2주차 강의를 쭉 들었습니다.머리가 좋지 않으면 복습을 바로 작성해도 그거 기억못할 게 뻔하니까요.. 여러번 들으니까 어느 정도 생각이 정리되면서 이해할 수 있었던 거 같습니다 😄(사실 이해못했을 수도 있는데.. 내 머리가 이해했다고 하니까 일단...)발자국부터 남기고 복습을 적으러가긴하는데.. 하하하.. 노력하겠습니다 이거 가지고 힘들어하면 그게 개발자겠습니까?! 안되면 되게 해야죠. 제 머리를 이해시키겠습니다!안되면 되게하라는 개발자의 논리를 그대로 적용시켜서 하겠습니다!# 내용 정리 BlogApplication 기능으로 이해하기 - ProbeApplication 기능으로 이해하기 - Configmap, SecretApplication 기능으로 이해하기 - PV/PVC, Deployment, Service, HPAComponent 동작으로 이해하기Probe 응용과제Configmap, Secret 응용과제PV/PVC, Deployment, Service, HPA 실습과제

데브옵스 · 인프라

이승환

[워밍업 클럽 4기 백엔드] 3주 차 발자국 👣

학습 내용 요약 Spring & JPA 기반 테스트 (Presentation Layer 테스트 (1) 까지)레이어드 아키텍처에서 각 레이어가 분리된 이유를 테스트 관점에서 이해할 수 있었습니다.cafeKiosk 프로젝트에 요구사항을 하나씩 추가해가며,Persistence → Business → Presentation Layer순서로 TDD 방식을 적용해 기능을 구현해보는 실습을 진행했습니다.  학습 내용 회고 🌱성취미션을 통해 총 15개의 단위 테스트를 작성하며, 테스트 작성에 익숙해질 수 있었습니다.boolean 변수명을 지을 때 자동사/타동사 구분을 고려하며, 더 명확한 이름을 짓게 되었습니다.레이어드 아키텍처의 레이어 분리 이유를 인식하고, 이를 기반으로 테스트를 구성할 수 있게 됐습니다.기능 전체를 TDD 방식으로 구현해보며 테스트 주도 개발에 대한 감을 익혔습니다.트랜잭션 전파 개념을 떠올리며 수동 클렌징과 자동 클렌징의 차이를 이해하게 되었습니다.@DataJpaTest와 @SpringBootTest의 차이점을 학습했고, profile을 통한 테스트 환경 분리에 대해서도 이해하게 되었습니다.   ⚠발견한 약점 및 보완이 필요한 부분TDD로 기능을 구현하는 과정에서, 중간 단계 테스트 작성을 종종 놓친 실수가 있었습니다.트랜잭션 전파, Spring AOP 개념 중 기억이 흐릿한 부분을 발견했습니다.@DisplayName의 명확하게 표현하는 데 어려움을 느꼈습니다.Day11 미션에서 Minesweeper 프로젝트의 단위 테스트 작성을 추가로 진행하지 못했습니다.  📄보완 계획TDD에 더욱 익숙해지기 위해 예제 프로젝트를 통한 실습을 추가로 진행할 예정입니다. Spring과 JPA의 작동 원리를 복습하며, 다음 항목들을 중점적으로 학습할 계획입니다.트랜잭션 전파Spring AOP JPA의 영속성 컨텍스트아직 작성하지 못한 Minesweeper 프로젝트의 단위 테스트를 작성합니다.예제를 통해 @DisplayName 작성에 더 익숙해지도록 합니다.  미션 내용 회고 Day11 미션 🌱 🏃첫 번째 미션[Readable Code] 강의의 두 프로젝트(지뢰찾기, 스터디카페) 중 하나를 골라, 단위 테스트를 작성해 봅시다.조건은 아래와 같습니다.✔각 프로젝트 모두 강의 중에 작성한 tobe 패키지 코드를 기준으로 함 (lesson 6-4 가 가장 마지막 버전)✔3개 이상의 서로 다른 클래스 & 총 7개 이상의 테스트 작성 (시간이 된다면 더 많이 작성해보면 좋겠죠? 😉)➡ 단, 같은 인터페이스를 구현하고 있는 구현체들은 1개 클래스로 간주한다.(ex. LandMineCell, NumberCell, EmptyCell에 각자 테스트를 작성했어도, 1개 클래스로 간주.)✔무엇을 테스트하고자 했는지를 잘 나타낸 @DisplayName 작성하기✔BDD(given/when/then) 스타일 따르기 (주석으로 표기) 🔗작업 브랜치 링크https://github.com/sonic8-8/readable-code/tree/day11-mission 미션 과정Day7 미션과 동일하게 사고 과정을 모두 Notion에 기록했습니다.다만 이번 미션에서는 문서 구조 자체의 가독성에 더욱 신경을 썼습니다.구체적으로는 다음 원칙을 적용했습니다.사고 과정은 하나의 흐름으로 기록하기 위해 Notion 문서에 작성한다.테스트 코드에는 주석을 통해 질문과 근거를 남긴다.  단위 테스트 작성 사고 과정🔗https://able-brick-82b.notion.site/Day-11-studycafe-20d2947a96118098986ad7e54c362460?source=copy_link 느낀점테스트 코드 역시 결국 읽기 좋은 코드여야 한다는 관점으로 미션을 수행했습니다.코드뿐만 아니라 문서 작성에서도 가독성을 어떻게 높일 수 있을지에 집중하다 보니,Notion이나 블로그 같은 일반적인 글쓰기에서도가독성을 높일 수 있는 방법에 대해 자연스럽게 고민하게 되었습니다. 주말 동안엔 이런 가독성 있는 코드 작성 방법에 대한 힌트를 얻고자 서점에 들렀고,클린 코드, 테스트 주도 개발, 파이브 라인스 오브 코드 등 책을 조금씩 읽어보며여러가지 인사이트를 얻을 수 있었습니다.특히 추상과 구체를 어떻게 분리해내는지에 초점을 두고 책을 읽으려했는데이 과정에서 코드의 구조를 바라보는 시야가 조금씩 확장되고 있다는 느낌을 받을 수 있었습니다. 다만, 책 분량이 많다 보니 단기간에 읽어내긴 쉽지 않다는 아쉬움도 있었습니다.그래서 워밍업 클럽이 끝난 이후에는 우빈님께 추천받은 위 서적들을 차근차근 학습하며,추상과 구체에 대해 더 고민하고 코드 작성 기본기를 탄탄히 다져나갈 계획입니다. 그리고 학습한 내용은 블로그에 저만의 방식으로 정리하고 포스팅 해서다른 사람에게도 도움이 될 수 있는 콘텐츠로 발전시켜 보고자 합니다. 🫡 강의🔗Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽백엔드발자국

양파쿵야

쿠버네티스 어나더 클래스 지상편: Sprint2 Day14 Helm과 Kustomize 2

Kustomize 구조Kustomize는 폴더를 수작업으로 생성해야한다.메인폴더디폴트 포맷 폴더kustomization.yaml ← 배포할 파일 선택 및 공통값 설정베이스 YAML 파일오버레이 영역 폴더배포 환경을 각 폴더로 구분해 선택적으로 배포할 수 있음kustomization.yaml ← 배포할 파일 선택 및 공통값 설정배포 환경에 맞게 오버레이 될 파일 배포할 파일 선택에 대한 차이점Helm: hpa.yml 파일에서 선언한 조건문이 values.yaml 파일에 의해 제어되어 배포됨Kustomize: kutomization.yaml 파일에 배포할 파일을 명시적으로 선언해 배포 공통값 설정에 대한 차이점Helm: YAML 템플릿 내에 다른 파일에서 변수를 주입됨Kustomize: Kustomization.yaml 파일 내 coomonLabels 키의 Lables가 모든 YAML 파일에 주입됨 환경별 설정에 대한 차이점Heml: values-dev.yaml, values-qa.yaml, values-prod.yaml 로 구별배포 명령에 -f 옵션으로 환경 파일를 선택: helm install api-tester-32222 ./app -f ./app/values-dev.yamlKustomize: 각 환경 폴더 dev, qa, prod 로 구별배포 명령에 폴더를 선택: kubectl apply -k ./app/overlays/dev  1. 다양한 배포 환경을 위한 Kustomize 배포하기 - 2222dev 개발환경이 선택되어 있다. -> Jenkins 파일 내에 있지만 처음 배포에 인식 안됨 dev 환경으로 배포 시작 실제 실무 환경에서는 K8s 네임스페이스 구분이 없어도 문제되지 않는다. 2. 다양한 배포 환경을 위한 Helm 배포하기 - 2223배포 파이프라인 구축 후 마주하게 되는 고민들컨테이너 빌드 후 도커 허브에 업로드할 때 사용되는 config.json에는 도커 허브의 사용자 정보가 저장된다. config.json 파일은 암호화가 되지 않기 때문에 인증 정보가 노출될 수 있다. Helm에서 사용하는 K8s 클러스터의 인증서는 리눅스 ROOT 권한으로 관리되어 인증서 파일을 복사하면 누구나 K8s 클러스터에 API 요청을 진행할 수 있다.⇒ CI/CD 서버의 접근 제어를 강화해야한다.⇒ 도커 허브 계정 정보와 K8s 클러스터 인증서를 Jenkins의 Credential로 등록하고 암호화하여 관리한다.⇒ 도커의 경우 Jenkins에서 암호화하더라도 config.json은 암호화되지 않는다, 배포시마다 로그인/로그아웃을 해 접속 정보를 삭제해야 한다.⇒ docker-credential-helpers는 config.json에 대한 암호화를 제공한다. 컨테이너 빌드가 지속적으로 진행되면서 CI/CD에 컨테이너 이미지가 누적된다. 서버의 디스크가 모두 사용되면 Jenkins 서비스가 중단된다.⇒ 도커 허브에 업로드 후 CI/CD 서버에 만들어진 이미지를 삭제한다. Helm을 통해 여러 App을 배포할 때 지난 실습처럼 App 하나마다 네임스페이스를 만들지 않고, 별도의 그룹 개념으로 App을 묶어 관리하는 경우가 많다.⇒ 네임스페이스는 배포와 별도로 관리하는게 좋다, Helm 앱을 삭제할 때 네임스페이스를 제거하면 네임스페이스에 포함된 리소스가 삭제되므로 Helm은 의도적으로 삭제하지 않는다. 배포 이후에 Pod가 완전히 정상적으로 기동됐는지 체크가 필요하다.⇒ Helm의 자주 쓰는 부가 기능으로 --wait 옵션을 사용한다. K8s는 Deployment의 Template 영역의 수정이 발생해야 업그레이드를 진행하므로 Helm 배포를 해도 업그레이드가 되지 않는 경우가 있다.⇒ Helm의 자주 쓰는 부가 기능으로 annotations를 사용해 새 배포시마다 랜덤 값 생성해 Helm 배포시마다 K8s가 업그레이드를 수행하도록 한다. 컨테이너 이미지의 태그 관리에서 개발환경은 잦은 배포를 진행하므로 versioning의 의마가 없지만, 검증/운영 환경은 계획된 배포를 진행해 versioning이 필수이다.⇒ 컨테이너 이미지의 버전 명명 규칙상 v를 제거하는게 맞다.⇒ 개발 환경은 이미지 태그로 latest를 사용하고, pullPolicy로 Always(항상 도커 허브에서 이미지를 가져온다)를 사용한다. 단, 인터넷 연결이 없으면 도커 허브에 접근을 못하므로 Pod 생성이 실패되고, Heml의 annotations을 사용하지 않는다면 이미지 태그가 latest로 고정되어 Template 수정이 없으므로 K8s가 업그레이드를 전혀 수행하지 못한다.⇒ 검증/운영 환경은 이미지 태그에 버전을 명시하고, pullPolicy로 IfNotPresent(K8s Node에 해당 이미지가 있으면 사용, 없으면 도커 허브에서 다운로드)를 사용한다. 운영 환경에서 Pod가 재시작되거나 스케일링이 작업 중인 상태에서 이미지를 도커 허브에서 다운로드받는 비효율적인 작업이 생략된다. 개발 환경에서 컨테이너 이미지에 대란 롤백을 요청하는 경우⇒ 개발 환경의 빌드부터 버전 관리를 시작해 개발자가 요청하는 버전으로 교체될 수 있도록 대응한다.매 배포시마다 이미지 태그가 변하므로 pullPolicy를 IfNotPresent로 변경해도 K8s에서 업그레이드를 수행할 수 있다.컨테이너 이미지를 받는 사람은 Latest가 단순 최신 개발 버전이 아닌, 운영환경까지 배포된 최신 안정화 버전으로 기대하고 다운로드 받는다.⇒ 프로젝트가 커질 수록 개발 이미지를 버전 관리하면서 바꿔가는게 좋다. CI/CD 서버와 마찬가지로 K8s 클러스터도 이미지를 계속 다운로드 받는다.⇒ Kubernetes Garbage Collector에서 사용하지 않는 이미지는 자동으로 삭제된다.  3. 배포 파이프라인 구축 후 마주하게 되는 고민들 2224 3-3-1. 중요 데이터 암호화 관리 3-3-1-1. 도커 접속 정보 및 쿠버네티스 config를 위한 New credentials 생성 ● Docker 접속정보 Credentials 등록 3-3-1-3. CI/CD Server에서 Docker Logout 및 kubeconfig 삭제 3-4. [파라미터와 함께 빌드] 실행 후 PROFILE을 [dev]로 선택하고 [빌드하기] 클릭 (빌드를 계속 하는 중,.,,,,)   

데브옵스 · 인프라워밍업클럽4기

Day2 추상과 구체 -Readable Code

 Readable Code에 대한 추상과 구체추상과 구체=추상(데이터)+구체(코드)ex) String(데이터)store(코드); =>프로그램=데이터(추상) + 코드 (구체) 1) 추상데이터가 추상에 해당하는 대표적인 표현중의 하나이다ex) char one=1byte //C언어 , 자바 2bytesex) int one=4byte //자바여기서 char, int, String, boolean... 등등이 추상이고 이 추상은 데이터타입에 해당한다. 2) 구체구체는 이 추상데이터타입을 구체적인 핵심개념표현으로 변수(코드)로 나타낸다ex) one, selectedColIndex 3) 추상과 구체가 문장으로 나타날 경우int selectedColIndex=convertColFrom(cellInputCol);=>여기서 int 는 추상 selectedColindex=convertColFrom(cellInputCol);=>구체(변수)에 해당하는 코드핵심개념표현이다 4) 추상과 구체가 메서드로 나타날 경우gameStatus=-1;=> changeGameStatusToLose();public static void changeGameStatusToLose(){return gameStatus=-1;}changeGameStatusToLose라는 의미는 -1은 게임실패를 의미하기때문에 그부분을 반영하여 최대한 구체에 해당하는 코드 핵심개념을 표현한다. 추상과 구체로 이름짓기 주의사항1) 스펠링 단수,복수 구분하여 축약하지 말고 이름 줄이지 말기2) 은어,방언 사용하지 않기3) 도메인 용어 사용하기=> 추상과 구체의 이름짓기목적: 읽기좋은 코드가 유지보수성과 가독성이 높아진다 참고사항-강의명 : Readable Code : 읽기 좋은 코드를 작성하는 사고법-지식 공유자 :박우빈 코치님-강의 링크: https://inf.run/pZXb7-스터디 : 인프런 워밍업 클럽 4기 BE 클린코드 & 테스트 회고사항추상과 구체가 그냥 늘상 형식적으로 쓰기만 했다 구체(변수)의 기능은 알고 있었다 과거에도 최대한 상대방이 내가 만든 프로그램은 어떻게 무슨 생각을 가지고 클래스를 만드는지 왜 호출하고 변수이름을 왜 이렇게 만들었는지 나를 뒤돌아아보게한다. 코드작성을 위한 흐름이 끊기지 않게 도메인설계의 핵심사항이자 기본원리를 이해해서 좋다. 

백엔드클린코드

3주차 회고록-Readable Code & Practical Testing

테스트 코드 적용실습-테스트코드와 함께 지난 시간 리팩토링을 같이 하고싶어 욕심을 냈다그러다 뒤에 practical-testing 분량을 한참바야한다는 것을 몰랐다가 나중에서야 알고나서 미션을 내기에 욕심과 잘못된 착각에 낼수가 없었다. 처음 for반복문에서 두번째 foreach를 따라하며 고민하고 좌절하며 따라하고 만들어보니 지난날 그냥 인강으로 볼때보다 이해와 흡수가 잘 되었다. 세번째로 stream이 아주 달리 보였다.자연스럽게 stream 을 쓸때에는 그에대한 클래스나 메서드가 완성되었을때는 사용할수 있었다. public List<ProductResponse> getSellingProducts() { List<Product> products=productRepository.findAllBySellingTypeIn(ProductSellingType.forDisplay()); return products.stream() .map(ProductResponse::of) .collect(Collectors.toList()); } 테스트 케이스에 따른 세분화하기테스트를 하면서도 어떻게 무엇을 써야할지 몰라 망설인다 우왕좌왕 하면서도 assertThat 써야하는곳의 전개과정을 보며 어떻게 돌아가는지 흐름과 진짜 반대로 버그가 일어날 만한 상황을 역으로 확인하는 부분에서 이해할 수 있었다.형식적인 테스트 부터 시작하여 경계값설정테스트는 소비자 입장에서 생각해볼 수 있는 부분이다@Test void addZeroBeverages(){ CafeKiosk cafeKiosk=new CafeKiosk(); Americano americano=new Americano(); assertThatThrownBy(()-> cafeKiosk.add(americano,0)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("음료는 1잔 이상 주문하실 수 있습니다."); }난 예외처리를 던지는건 알겠는데 () -> 이게 표현이 다소 생소하였다. 값을 반환할때 저렇게 쓴다는걸 우테코유트브를 보다 알았다 나한테 아주 요상하게 생긴 녀석이었다 머야 괄호를 던지나???? assertThatThrownBy(()-> cafeKiosk.add(americano,0))  DisplayName 은 섬세하게 displayName을 굳이 왜써야하나 생각해보았다한글이 지원되어 무엇보다 도메인사용에 대해 이해할수 있게 명확하게 쓰기 최적의 명령어였다.모르는 사람이 바도 테스트의 목적을 이해를 돕는 문서의 결과가 될수 있다는 생각에 공감이 간다.-백날 영어쓰고 설명이 없다면 진짜 영어로 바도 이해가 쉽다면 상관없다. 그러나 만드는 개발자도, 기획자도 관리 유지에 있어 모호한 부분의 확장에선 둘다 필요하다 Spring Jpa & persistence Layer 포트폴리오를 만들기위해 애썼던 지난 시간들이 떠올랐다. 당시도 작년도 난 눈앞에 내용을 쳐내려가기 바빴다 영속성관계를 깊이 있게 보질 못했다. 이번에 보다 보지못한 지나쳤던 부분을 이해하게 되었다.@OneToMany(mappedBy="order")이 어노테이션은 양방향 관계에서 사용되며 mappedBy 는 주체가 아닌쪽에서 관계를 관리하는 필드를 지정한다@Entity public class Order { @Id private Long id; @OneToMany(mappedBy = "order") private List<OrderItem> items; } @Entity public class OrderItem { @Id private Long id; @ManyToOne @JoinColumn(name = "order_id") private Order order; }@MapsId 와 @OneToOne(일대일관계)@OneToOne 두 엔티티 간의 일대일 관계를 정의한다 @MapsId@OneToOne 관계에서 같이 사용되며 두개체가 동일한 기본키를 공유할 때 그 관계를 설정하는데 사용된다@Entity public class UserProfile { @Id private Long id; @OneToOne @MapsId private User user; }아주 비슷하게 생겨서 처음엔 헷갈렸는데 영어보구 알았다. 눈을 크게 뜨고 바야겠다. @SpringBoot VS @ DataJpaTest@DataJpaTest-DataJpaTest 관련 기능중에 @Transactional 기능이 포함되어 rollback 기능이 되어 완료후 초기화를 할 필요하 없다는 사실은 나에게 새삼 놀라웠다. 우라질....Transactional 에 rollback 기능이 있다는것은 알고 있었다 그런데 그 뿌리가 @DataJpaTest 안에도 포함된 내용인지는 새롭게 알았다import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest @Transactional public class OrderRepositoryTest { @Autowired private OrderRepository orderRepository; @Test public void testSaveOrder() { Order order = new Order(); order.setProductName("Test Product"); order.setQuantity(1); orderRepository.save(order); // 데이터베이스에서 주문을 조회하여 확인 Order foundOrder = orderRepository.findById(order.getId()).orElse(null); assertThat(foundOrder).isNotNull(); assertThat(foundOrder.getProductName()).isEqualTo("Test Product"); } } @Transanctional 과 @ SpringBoot (일반적인 로직) SpringBoot 는 Transactional 과 같이 있을경우에는 rollback기능이 작용한다import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { private final OrderRepository orderRepository; public OrderService(OrderRepository orderRepository) { this.orderRepository = orderRepository; } @Transactional public void createOrder(Order order) { // 주문 생성 로직 orderRepository.save(order); // 추가적인 비즈니스 로직 } }  한시간 강의를 듣는데도 정리하고 이해하는데 4-5시간이 걸려 후딱갔다 나한테는 너무 많은 강의량에 눈이 빠질거같다..머리에 구멍난 부분은 차곡차곡 정리되어 가는데 다른분들은 그 많은 강의를 듣고도 스터디에 올려서 난 그러고도 분량이 한참 남아 올리지를 못했다. 내가 하고 싶은 욕심과 내가 소화할 수 있는 부분에 대한 분량에 대해서 시간제한, 자원배분에 대해 생각해보는 한주였다. 참고사항강의 practical testing -박우빈 코치님우테코 람다,오브젝트 유트브-저 닉네임 또 까먹었습니다. 죄송합니다.  

테스트코드persistenceLayerspringJPA

이상현

[인프런 워밍업 클럽 4기] DevOps 발자국 3주차

[ 강의 정보 ]강의명 : 쿠버네티스 어나더 클래스 (지상편) - Sprint 1, 2 [ 워밍업 클럽 3주차 회고 ]3주차쯔음 들어가니 슬슬 밀린 복습들이 생겨나고 있다.. 평일에 부지런히 하면 좋았을련만 주말에 2주차에 밀린 것까지 하려니 시간이 너무 없다 @.@ 평일에 저녁약속이다 뭐다 여러 가지일로 미뤄온 결과라고 생각하며 열심히 달려본다 흑흑 이번 주차에서는 CI/CD 구축과 함께 Recreate, RollingUpdate, Blue/Green, Canary 와 같이 더 다양한 배포전략을 알아보았다. Jenkins를 설치하는 과정에서 아래의 약간의 에러(?)가 있었는데 Gemini의 힘을 빌려 생각보다 빠르게 해결할 수 있었다. 맞닥뜨린 에러와 해결방법은 블로그에 짧게 기재두었다. 바로가기 👉👉https://velog.io/@zxd985/인프런-워밍업-클럽-4기-DevOps-3주차-2처음에는 인터넷 서치로 찾아보려 했으나.. 키워드를 잘 설정하지 못한 것인지 잘 안나와서 결국 AI의 힘을 빌렸다ㅎ..이전에는 이미 설치되어 있는 Jenkins 서버에서 빌드 버튼만 딸깍. 했었는데 직접 설치부터 Item 생성 > Build Step 구성까지 모두 직접해보니 어떻게 Jenkins CI/CD가 동작하는 것인지 흐름을 이해할 수 있었다. 배포 전략 중에서는 Canary 배포가 현업에 매우 필요하다고 느꼈는데 한번에 트래픽을 넘기는 것이 아닌, 일정 비율의 트래픽만 전환하면서 에러의 여부를 확인하는 것이 서비스를 이용하는 고객에게 오류를 덜 노출할 수 있고 빠르게 롤백할 수 있을 것 같다고 생각했기 때문이다. 또한, A/B 테스트 등 헤더를 활용해 특정 고객에게만 신규 버전을 노출하는 등 여러 비즈니스를 테스트하기에 적합할 것 같다. 다만 istio와 같은 서비스 메시도 알아야 한다는 크나큰 산이 ....이래 저래 정신없이 3주차가 지나가고 있는데, 과연 내가 스터디를 잘하고 있나 라는 의구심도 들면서도... 그래도 마지막까지 잘 달려보고 싶다는 생각이 든다. 조금만 더 화이팅 해보자! 인프런 워밍업 클럽 4기 블로그 시리즈

kubernetesdevops

3주차 발자국

1. 학습 회고3주차 키워드: Layer별 테스트day11 - 단위 테스트 작성 미션day12 - Persistence Layerday13,14 - Business Layerday15 - Presentation Layer Persistence Layer: DB 접근 계층, 단일 테스트와 유사함. 단순 DB 조회도 왜 테스트가 필요한지에 대해서도 깨달음.Business: 서비스 로직 계층. persistence layer가 포함된 통합 테스트 작성, Spring 을 통째로 띄워서 테스트.Presentation: 외부 세계의 요청을 받는 계층. 파라미터에 대한 최소한의 검증. Persistence/Busuness Layer는 Mocking 하고 단위 테스트 형태로 진행.Mock 을 쓰는 이유: 테스트를 하는데 의존관계가 걸림돌 -> Mock 으로 해결 테스트 강의도 좋았지만 jpa 에 대해서도 한 번 짚고 넘어가는 강의여서 더 좋았다.처음 발자국 작성한 게 얼마 전 같은데 벌써 3주차가 끝났다.마지막까지 가보자고! 미션 회고day 11 미션은 "Readable Code 강의에서 진행한 프로젝트에 단위 테스트 코드를 작성하기" 이다.이전 리팩토링 미션에서 스터디카페를 했으니, 테스트 코드도 여기에 해보면 좋을 거 같아서 스터디카페 프로젝트에 단위 테스트를 작성해보았다. 미션을 진행하면서 고민한 것들은.. 어떤 경우에 테스트 코드를 작성하면 좋을지, 만들어진 모든 메서드에 대해서 작성해야하나? 아니면 정말 중요한 것들에만 작성해야하나? 하는 것이었다.그 결과, 중요해보이는 녀석들로만 선정을 했는데 다음 고민이 생겼다.검증은 어떤 값을, 어느 정도 수준으로 해야하는지, 너무 뻔한 걸 검증하는 건 아닌지, BDD는 잘 따랐는지 등,,아직은 배우는 단계니까 작성을 해 보는 것에 의의를 뒀다. 먼저 이용권 목록은 파일에서 읽어오는 형태이니, 좌석이용권/사물함이용권의 종류를 확인하면 좋을 것 같아서 테스트 코드를 작성했고, 고정석일때만 사물함을 선택 가능한 것도 중요한 조건인 것 같아서 테스트 코드를 작성했다. 그리고 가장 중요한 금액 계산.할인 이벤트가 기간별로 잘 적용되는지, 사물함 이용권까지 할인이 되지는 않는지에 대한 테스트 코드를 작성하는 것을 마지막으로 미션을 제출했다.   학습 출처인프런 워밍업 클럽 스터디 4기 - 백엔드 클린코드, 테스트 코드수강 강의:Practical Testing: 실용적인 테스트 가이드 - 박우빈

워밍업 클럽 4기 백엔드 - 3주차 발자국

강의 내용3주차에는 Practical Testing: 실용적인 테스트 가이드 강의의 Spring & JPA 기반 테스트 섹션을 수강했다.Layered Architecture와 테스트Presentation Layer - Business Layer - Presentation Layer 각 계층에 대한 테스트 방법테스트 하기 복잡해 보이지만, 기본적으로는 단위 테스트와 비슷테스트 하기 어려운 부분을 분리해 테스트 하고자 하는 영역에 집중하고, 명시적이며 이해하기 쉬운 테스트 코드 작성어떤 기술을 사용하는지가 중요한 게 아니고, 어떻게 테스트할 것인지에 집중여러 모듈, 여러 객체가 통합해서 동작하는 경우, 단위 테스트와 별개로 통합 시에 어떻게 동작하는지를 확인하기 위한 통합 테스트가 필요Persistence Layer영속성 계층은 Data Access 역할 수행비즈니스 로직이 포함되어서는 안 되며, CRUD 그 자체에만 집중 Business Layer비즈니스 로직을 담당트랜잭션 보장의 책임을 지님Persistence Layer와 연계하여 통합 테스트 느낌으로 테스트 작성Presentation Layer외부 세계의 요청을 가장 먼저 받는 계층Presentation Layer 테스트 시 가장 집중해야 할 부분은 파라미터에 대한 검증하위 레이어들이 정상적으로 동작할 것을 가정하고 Mocking 하여 테스트 수행Mock MVC: 목 객체를 이용해 스프링 MVC 동작을 재현할 수 있는 테스트 프레임워크  회고강의를 수강하며 테스트 코드를 사실상 처음 작성해본 입장에서 이번 3주차에 들은 강의는 정말 많은 도움이 된 것 같다. 3주차 강의를 들으면서는 강의를 멈춰 놓고 먼저 테스트 코드, 기능 구현을 해보고 강의를 재생해 확인하는 방식으로 공부했다. 테스트 코드를 직접 작성해보고 강의를 듣다 보니 왜 테스트 코드를 짜야 하는지 확실히 체감할 수 있었다. 코드 변경 시에도 테스트 코드를 통해 수시로 확인해볼 수 있어 한 층 편리하기도 하고, 어떤 테스트를 수행해야 할지 고민하는 과정에서 생각지 못 한 예외 케이스를 떠올릴 수 있었다. 복습하는 과정에서 작성했던 테스트 코드를 읽으면서 왜 테스트 코드도 문서라고 하셨는지도 이해했다.테스트 코드 강의를 수강하면서 생긴 고민이 한 가지 있는데, 여건 상 실무에서 테스트 코드를 작성하기가 쉽지 않다는 점이다. 지금 재직 중인 회사에서 '테스트'라는 건 Postman으로 API 호출해서 응답이 잘 돌아오고 데이터베이스에 값이 잘 들어가는지 확인하는 것, 브라우저로 개발한 페이지에 접속해서 기능 사용해 보는 과정을 의미한다. 물론 필요하지만, 뭐 하나 고칠 때마다 똑같은 걸 계속 하려다 보니 피로감이 컸다. 저번주 금요일에 들었던 테스트 강의 초반부에 정확히 동일한 내용이 나와서 이번주 강의 내용을 정말 기대했는데, 기대한 것 이상의 가치가 있는 내용이었다고 생각한다. 단순히 반복되는 수동 테스트의 피로도를 낮추는 것을 넘어서 코드 품질을 높일 수 있는 정말 좋은 방법 중 하나인 것 같다. 테스트 코드 도입은 어려울 것 같지만, 그렇더라도 의식적으로 테스트 용이성을 신경쓰면서 테스터블한 스타일로 개발하려는 노력 정도는 얼마든 해볼 수 있을 것 같다. 미션Day 11Day 11 미션은 Readable Code 강의에서 작성한 소스의 단위 테스트를 작성하는 것이었다. 미션 수행에 있어 '테스트가 필요하다고 판단하는 과정부터 테스트의 시작'이라는 조언을 주셨는데, 미션을 시작하려던 순간에 정말 공감되는 조언이었다. 테스트 코드 작성이 익숙하지 않았지만, 나름대로 중요한 비즈니스 로직이나 추후에 변경될 가능성이 있는 부분을 찾아 테스트 코드를 작성해보려고 노력했다. 가장 어려웠던 작업 중 하나는 @DisplayName을 작성하는 것이었다. 도메인 용어를 사용한 추상화 된 내용을 작성해야 하며, 도메인 정책의 관점으로 설명을 작성해야 한다고 했지만, 미션에서 작성한 단위 테스트는 워낙 간단한 내용들이라 처음에는 갈피를 잡지 못 했다. 얼마나 자세히 써야 할지, 자세히 쓴다고 썼지만 너무 구현 측면의 세부사항에 의존한 설명은 아닌지 계속 고민이 되었다. 많은 연습과 고민이 필요할 것 같다는 생각이 들었다.

백엔드

인프런 워밍업 클럽 4기 BE - 3주차 발자국

✅ 강의 수강학습 요약Layered Architecture컨트롤러, 서비스, 리포지토리 등 계층 간 책임을 명확히 나누는 구조 Hexagonal Architecture애플리케이션의 핵심 도메인을 중심에 두고, 외부 의존성(입출력, DB 등)을 포트와 어댑터로 분리해 유연성과 테스트 용이성을 높인 구조 패러다임 불일치객체지향 프로그래밍과 관계형 데이터베이스의 개념 차이에서 발생하는 불일치로 객체의 참조와 DB의 외래 키 간의 차이를 적절히 매핑해야 한다.Spring Data JPA기본적인 CRUD 쿼리를 인터페이스 정의만으로 제공하며 복잡한 쿼리는 직접 구현하거나 QueryDSL 등을 활용할 수 있다.QueryDSL자바 코드 기반으로 JPQL을 작성할 수 있게 해주는 라이브러리로 타입 안정성과 동적 쿼리 작성에 유리하다. @Transactional(readOnly = true)읽기 전용 트랜잭션 설정으로, 변경 감지를 비활성화해 성능을 최적화할 수 있다.Optimistic Lock / Pessimistic Lock낙관적 락은 충돌이 없을 것이라 가정하고 버전 번호로 변경을 감지하고, 비관적 락은 먼저 락을 걸어 동시 수정을 방지한다.CQRS명령(Command)과 조회(Query)의 책임을 분리하는 아키텍처 패턴으로 복잡한 읽기/쓰기 로직을 분리해 성능과 유지보수성을 높일 수 있다.@RestControllerAdvice / @ExceptionHandler공통 예외 처리를 위한 방법으로 커스텀 예외를 만들어 처리할 수도 있다.Bean Validation@NotBlank, @NotNull 등의 어노테이션을 통해 요청값에 대한 유효성 검사를 명시적으로 정의할 수 있다.Mockito / @MockBean테스트 시 외부 의존성을 가짜 객체로 대체해 원하는 동작을 검증하거나 테스트 범위를 좁히는 데 사용된다.  학습 회고그동안 스프링과 테스트 코드에 관련해 파편적으로만 알고 있던 지식들을 조금 더 체계적으로 정리할 수 있었다. 완전히 처음 접해보는 것이 아닌 개념들이라도 제대로 공부하지 않은 채 그냥 가져다 쓰기만 했던 어노테이션이나 설정의 의미에 대해 생각해보는 계기가 되었다. 시간을 내어 수강하는 것이 쉽지만은 않지만 어떻게든 흐름을 따라가다보면 결국 얻는 것이 있을거라 믿는다. ✅ 미션해결 과정어디부터 시작할지 고민하다가 BoardIndexConverter, CellSnapshot, GameBoard처럼 눈에 띄는 핵심 클래스를 골라 간단한 메서드부터 테스트 코드를 하나씩 작성해보았다. 정상 동작을 확인하는 것부터 시작해 예외 상황까지 케이스를 넓혀가면서 어떤 흐름으로 접근해야 하는지 조금은 감을 잡을 수 있었다. 내부 구현에 너무 의존하지 않고 각 메서드가 맡은 역할과 책임에 맞게 동작하는지를 중심으로 테스트 코드를 작성하려 의식하며 미션을 수행해 보았다. 미션 회고전체 코드에서 어떤 부분을 골라 먼저 작성할 지 생각하는 것부터가 쉽지 않았지만 작은 단위부터 차근차근 확인해 나가면서 테스트 작성 흐름에 약간씩 익숙해질 수 있었던 것 같다. 가독성 있는 테스트 네이밍이나 @DisplayName을 정하는 부분에서 생각보다 많은 고민이 필요해 역시 네이밍이 가장 힘든 부분이라는 걸 다시 한 번 느끼는 시간이었다.

백엔드인프런워밍업클럽

livoi

[워밍업 클럽 4기 - 백엔드] 3주차 회고

인프런 워밍업클럽 백엔드 스터디를 진행하며 3주차 회고를 정리해봤습니다. KEEPPersistence Layer, Business Layer에 대해서 코드를 구현하면서 테스트 코드를 함께 작성하는데 학습을 하면서 그동안 테스트코드를 작성하면서 느꼈던 의문을 많이 해결하는 시간이 되었습니다. 테스트코드에서 어떤 계층을 우선적으로 테스트 해야할지, 어떤 관점에서 테스트코드를 바라봐야 할지, 그리고 서비스로직에 대해서 테스트코드를 작성하면서 테스트를 위해 구축해야 하는 부분들이 어디까지 일지 하는 고민들이 있었는데, 그런 부분에 대해서 강의에서 다뤄주셔서 공감하면서 강의를 수강했습니다.  PROBLEM지난주와 비슷한 생각이지만 강사님이 실시간으로 막힘없이 작성하실만큼의 시간들이 아직 저에게는 없기 때문에 여전히 낯선 부분들이 있어서 테스트코드를 중요한 메서드를 선정해서 적용해보는 연습이 필요할 것 같습니다. 그리고 TDD를 연습해봤는데 red-green을 오가면서 하는 부분에 대해서도 작게나마 적용해 보고 싶습니다.TRYAssertJ 를 사용하면서 assertThat, assertEquals만 사용했었는데 새로운 메서드를 많이 알게 되었습니다. 새로운 코드를 작성할때 내가 사용할 수 있을 정도로는 아직 익숙해지지 않아서 더 많이 사용해 보는게 필요하고, 강의에서 말해주신것 처럼 테스트가 가능한 영역과 가능하지 않은 영역을 분리해서 테스트코드를 작성했을때 효과적인 부분들을 분리하는 눈도 더 길러가고 싶습니다.

백엔드인프런워밍업클럽실용적인테스트가이드

이강호

워밍업 클럽 4기 백엔드 - 3주 차 발자국

👣3주 차 발자국 Practical Testing: 실용적인 테스트 가이드 ✅ 강의 요약섹션 6. Spring & JPA 기반 테스트 Layered Architecture단점 : 기술에 대한 강결합 통합테스트단위테스트 만으로는 기능 전체의 신뢰성을 보장할 수 없다. (여러 모듈 및 여러 객체가 협력하기에) 여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트Spring / JPA 훑어보기 & 기본 엔티티 설계 IoC (Inversion of Control)객체의 생성과 소멸, 생명주기 관리를 제 3자가 하는 원리DI (Dependency Injection)필요한 객체를 직접 생성하는 것이 아닌 외부에서 생성해 주입하는 것IoC이 DI로 실현된다.AOP (Aspect Oriented Programming)비즈니스 흐름과 관계없는 공통 기능 (트랜잭션, 로깅 등) 을 분리해 모듈화 시키는 것을 의미스프링에서는 Proxy형태로 제공ORM (Object-Relational Mapping)객체 지향 패러다임과 관계형 DB 패러다임의 불일치를 해결 JPA (Java persistence API)Java 진영의 ORM 기술 표준JPA - 인터페이스, 구현체 - Hibernate (대표적으로 사용됨)Spring에서는 JPA를 한번 더 추상화한 Spring Data JPA 형태로 제공주로 QueryDSL과 같이 사용됨  Persistence Layer 테스트@DataJpaTestJPA 관련 컴포넌트만 테스트할 때 사용하는 전용 어노테이션@SpringBootTest보다 가볍다.@Transactional이 있어 롤백이 된다.그치만 선호하지 않는다.Persistence LayerData access의 역할만 해야한다.Data의 CRUD에만 집중한 레이어.Business Layer 테스트비즈니스 로직을 구현하는 역할Persistence Layer와의 상호작용을 통해 개선 (통합적 테스트)트랜잭션을 보장해야 한다.@WebMvcTestPresentation Layer에 대한 단독 테스트시 사용하는 어노테이션@Transactional(readOnly = true)읽기 전용JPA에서는 스냅샷 저장, 변경감지가 일어나지 않아 성능 향상 read only값을 보고 DB의 endpoint를 분리할 수 있다. (true일 경우, read 전용 DB로 보낸다.)구분을 잘 하는 것이 중요하다.readOnly = true 구분이 누락될 수 있기에 아래 방법을 사용.service class 상단에 readOnly = true를 걸고, CUD 작업이 있다면 메서드 단위에 @Transactional을 건다.CQRS읽기(조회)와 쓰기(명령)의 책임을 분리하는 소프트웨어 아키텍처 패턴Command(CUD) 와 Query(Read) 의 책임을 분리하자. (read가 훨씬 많이 일어난다.) 성능 최적화, 비지니스 로직의 분리, 장애 격리를 할 수 있다. ✅ 강의 회고WebMvcTest의 사용과 Transactional(readOnly = true)의 중요성을 알게 되었다.그리고 @SpringBootTest는 데이터 클랜징이 필요 하기 때문에 클랜징 때문에 일어나는 문제를 유의해서 봐야하는걸 배웠다. 데이터 세팅에 손이 많이 가기 때문에 테스트가 귀찮다고 느껴지는 것이 아닌가 싶었다. ✅ 미션 과정 & 회고미션 PR - link우선 스터디 카페 이용권 선택 시스템을 기반으로 미션을 진행했다.강의 내용처럼 @DisplayName을 최대한 자세히 작성하려고 했고, 미션 조건처럼 3개 이상의 서로 다른 클래스를 만족하기 위해 기능이 다른 클래스를 선택해 작성했다.그리고, BDD (given/when/then) 스타일을 따라 작성했다.강의 내용처럼 테스트가 '성공한다' , '실패한다' 의 의미를 포함하지 않게(테스트의 현상을 포함하지 않게) DispayName을 작성하려 했고, 최대한 도메인에 관련된 이름을 넣어서 표현했다. TDD 방식으로 작성한 테스트가 아니기 때문에 해피케이스만 작성된 것 같다. 아무래도 이에대한 보완은 중간점검때 확인해 봐야 할 것 같다. 강의 - Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽4기워밍업클럽백엔드

워밍업 클럽 4기 - 3주차 발자국

학습편무엇을 학습했는가?테스트 코드에 관한 관점, 테스트 코드 범위에 대한 생각을 학습했습니다.개인적으로 좋았던 부분은 JPA를 사용하면서 코드 자체가 JPA에 귀속되어 Hexagonal 아키텍처가 등장한 배경을 설명해주신 부분이었습니다.  어떻게 학습했는가?이전 강의(Readable Code)의 경우에는 강의에서 치는 코드를 같이 쳐가면서 학습했습니다.실제로 리팩토링을 진행하면서 코드에 감을 익히기 위함이였습니다.코드를 쳐가면서 코드를 작성하는 법을 체득하기 위함입니다.이번 강의에서는 코드를 치기보단 강의를 듣는 방식으로 학습하고 있습니다. 테스트 코드를 작성하는 게 낯설지가 않아서 이 방법을 택했습니다.이번 주는 이전에 들었던 강의를 다시 들으면서 쉬어가는 주였습니다.2주차 때 많이 무리한 것 같습니다... 출근해야하는데 새벽 1시까지 강의를 들었으니... 잘한 점지치지 않고 주말에 꾸준히 강의를 듣는다는 점이 잘했습니다. Readable Code와 학습 방식을 바꿨는데, 바꾼 방법으로 학습해도 이해가 잘 됩니다. 아쉬운 점주중에 강의를 계속 못듣는다는 점이 아쉽습니다. 다만 제 상황(PR 리뷰, 회사)상 어쩔 수 없는 부분이라고 생각합니다. 개선할 점이대로 계속 학습하면 무사히 완주할 수 있을 것 같습니다. 미션편어떤 미션을 수행했는가? Readable Code의 지뢰찾기 게임에 테스트 코드를 작성했습니다.  어떻게 미션을 접근했는가?이번 미션에서는 핵심 비즈니스 로직을 테스트 하려고 했습니다. 스스로 난이도를 높여서 테스트 하기 힘든 Board를 테스트했습니다.지뢰 Cell을 선택하는 로직이 로직이 Board안에 포함되어 테스트하기가 힘들었고, 지뢰 선택 방법을 Interface로 선언, 리팩토링하여 외부에서 지뢰 선택 방법을 주입하는 방식으로 변경했습니다.  잘한 점테스트 코드 미션이였지만 Readable Code 강의에서 배운 걸 적용하여 지뢰찾기 로직을 리팩토링했습니다. 코드를 보는 눈이 좀 나아진걸지도...?스스로 난이도를 상향시켜서 로직을 테스트 했습니다.제 나름의 미션이긴한데, 회사의 결제 로직을 통합했습니다. Readable Code에서 배웠던 것들을 바로 적용해 볼 수 있어서 좋았습니다. 아쉬운 점이제는 인정해야겠습니다. 절대적인 미션 수행 시간이 부족합니다.(미션 떄문에 연차를 쓰기는 좀...)이번 미션은 2시간 동안 수행했는데, 리팩토링할 때 시간을 많이 사용해 시간 내 미션 제출을 못할 뻔했습니다. 난이도를 높여서 미션을 수행한 점은 좋았으나, 시간 분배에 실패하여 미션 제출을 못할 뻔했습니다.개선할 점시간이 없는걸 이제 그만 인정하고 시간에 맞는 미션을 수행합시다. 

[워밍업 클럽 4기 BE] 3주차 발자국

🌱 배운 내용이번 강의 내용은 실습이 많기 때문에 기억에 남았던 말들과 느낀점을 정리해 보겠습니다. ✅[ 비지니스 레이어 ] " 이렇게 작은 메서드도 테스트 해야 할까요? -> " 네. 해야 합니다. " 재고 감소와 같이 작은 도메인 메서드도 테스트를 해야 하는지에 대한 질문과 답변 입니다.도메인 로직은 변할 수 있기 때문에 테스트를 통해 관리 해야 한다고 말씀 하신 부분이 기억에 남습니다.프로젝트를 하며 도메인 검증을 하지 않아 문제가 생긴적이 있었는데 반성하는 기회가 되었습니다. " 재고 차감을 비지니스 레이어, 도메인 둘다 체크 하고 있는데 둘 다 해야 할까요?" -> " 네. 해야 합니다. " 재고 차감을 할 때 비지니스 레이어, 도메인 각각 체크 하여 예외를 터트리는 상황에서 하신 말씀과 답변 입니다.이 부분도 마찬가지로 프로젝트를 하며 " 이 메서드는 도메인 로직일까?... 비지니스 로직 일까?.."고민 하며 하나만 체크 하려고 했습니다.강의를 통해 이둘은 다른 것임으로 따로 체크 하는 것이 좋구나 라고 생각 했습니다. ✅ Day 11 미션 회고 강의를 보며 코드를 짤 때는 " 그렇게 어렵지 않네? " 라고 생각 했습니다.하지만 미션을 해보고 느낀점은 " 고민할게 많구나 " 였습니다." @DisPlayName은 어떻게 작성 해야 내 의도를 잘 나타낼 수 있을까? "" 어떤 테스트를 해야 할까? "" 내가 놓친 부분은 없을까? " 등을 생각 하며 테스트 한개를 작성할 때도 많은 고민이 필요 했습니다.미션을 함으로써 테스트 코드에 대해서 고민을 해보는 시간을 가질수 있어 좋았습니다.!   

백엔드백엔드

채널톡 아이콘