AWS에 project 와 team 이라는 기능이 새로 생겼다고 해서 공식 문서를 읽고 간단하게 정리했습니다. AWS News Blog에 실린 신규 가입 화면입니다. 로그인 버튼 아래에 기존 방식으로 가는 Sign up (advanced) 링크가 있습니다. (출처: AWS reimagines the getting started experience , 콘솔 화면은 모두 같은 글에서 가져왔습니다) ⚠️ 제한 릴리스 공식 문서 전부에 신규 고객 일부에게만 배포 중이라는 경고가 붙어 있습니다. 🧐 Project는 무엇인가 AWS 계정 1개에 공유 설정이 붙은 단위입니다. 가입하면 첫 프로젝트가 자동으로 만들어지고, 루트 사용자 설정, IAM 설계, 리전 선택은 AWS가 대신 정합니다. 관리 콘솔은 기존 콘솔과 따로 https://settings.aws.com 을 씁니다. Projects 화면에는 프로젝트 수, 팀원 수, 무료 플랜 잔여 일수, 남은 크레딧이 나옵니다. 프로젝트를 여러 개 만들면 모두 하나의 organization에 들어가는데, 이 organization은 일반 AWS Organization과 같은 것입니다. 조직 정책(SCP/RCP)은 AWS가 관리하고, AWS Settings에서 고급 기능 활성화(Activate advanced features) 를 하면 내가 조직 관리자가 되어 직접 관리합니다. 이때 project는 조직의 멤버 계정으로 관리됩니다. 유료 플랜으로 먼저 업그레이드해야 하고, 활성화는 되돌릴 수 없습니다. 🗺️ 리전은 고를 수 없다 프로젝트를 만들 때 리전을 고르는 화면이 없습니다. 연락처 주소의 국가를 보고 AWS가 세 리전 중 하나를 정해 줍니다. 리전 대상 지역 US East (Ohio) us-east-2 미국, 캐나다, 중남미, 카리브 Europe (Stockholm) eu-north-1 유럽, 중동, 아프리카 Asia Pacific (Sydney) ap-southeast-2 아시아 태평양 (대한민국, 일본, 싱가포르, 인도 포함) 한국은 Sydney 표에 있어서, 한국에서 가입하면 서울이 아니라 시드니에 리소스가 만들어집니다. 연락처를 바꿔도 한 번 정해진 리전은 그대로입니다. 🔒 프로젝트 안팎의 접근 기본적으로 같은 프로젝트 안의 코드와 애플리케이션은 그 프로젝트의 모든 AWS 리소스에 접근할 수 있습니다. 다른 프로젝트의 리소스끼리는 접근할 수 없고, 필요하면 cross-project access를 켭니다. 인터넷에서 오는 접근도 기본으로 막혀 있어서 공개하려면 퍼블릭 S3 버킷, API Gateway 같은 것을 직접 구성해야 합니다. 💰 spend limit 유료 플랜에서는 프로젝트별로 월 비용 상한(최소 20달러)을 걸 수 있습니다. 한도에 가까워지면 알림이 오고, 한도에 도달하면 프로젝트가 일시정지됩니다. 고급 기능을 활성화하면 spend limit이 모두 사라지고 다시 만들 수 없습니다. 👥 Team은 무엇인가 IAM 사용자를 만들지 않고 이메일 초대로 협업자를 추가하는 기능입니다. Team 페이지나 프로젝트의 Members 화면에서 이메일과 공유할 프로젝트를 고르면 됩니다. 초대받은 사람은 AWS Builder ID를 만들어서 14일 안에 수락해야 합니다. 프로젝트를 공유할 때 서비스별로 접근 권한을 나눌 수는 없습니다. 소유자와 팀원 모두 그 프로젝트의 admin이고, 차이는 재무와 관리 작업에서 납니다. 팀원이 할 수 있는 것: 프로젝트 리소스 조회, 생성, 수정, 삭제 / AWS Settings 접근 / spend limit 조회 / 서비스 할당량 증설 요청 소유자만 할 수 있는 것: 팀원 초대와 제거, 프로젝트 이름 변경, 결제 수단과 청구, 세금, 크레딧 관리, 유료 플랜 전환, spend limit 설정, 루트 전용 작업 콘솔과 AWS Settings에서 한 작업은 CloudTrail에 초대받은 Builder ID( onBehalfOf )와 함께 기록되어, 누가 바꿨는지 확인할 수 있습니다. 🎯 이 기능의 목적 AWS News Blog는 설정 작업을 끝내야 프로젝트를 시작할 수 있는 방식 대신, 기본값으로 바로 시작하게 하는 기능이라고 설명합니다. IAM이나 IAM Identity Center를 몰라도 이메일만으로 협업자를 초대할 수 있습니다. 가입 직후 코딩 에이전트에 붙여넣을 프롬프트를 줍니다. 실행하면 에이전트가 AWS CLI와 Agent Toolkit for AWS를 설치하고 로그인합니다. 콘솔 작업과 코딩 에이전트가 지원 서비스와 리소스 사이의 권한을 자동으로 설정합니다. 콘솔에서는 IAM role manager가 역할을 만들어 줍니다. role manager가 만든 역할을 엄격하게 조이려면 role manager를 끄고 IAM Access Analyzer로 조정하라고 AWS가 안내합니다. 이 둘은 고급 기능을 활성화해야 쓸 수 있습니다. ⚖️ GCP Project와 비교 GCP project는 organization과 folder 밑에 있는 계층의 기본 단위이고, AWS 신규 경험의 project는 AWS 계정 1개입니다. 차원 GCP Project AWS Project 계층 organization -> folder -> project organization -> project 리전 리소스마다 자유 선택 불가, 연락처 국가로 3개 중 자동 배정 권한 부여 IAM 역할을 principal에 부여, 서비스 단위 제어 가능 프로젝트 단위 admin 공유, 서비스 단위 제어 없음 과금 별도의 Cloud Billing 계정에 연결 프로젝트가 곧 계정, 청구 권한은 소유자에게 프로젝트 안의 접근 IAM 역할로 제어 기본은 같은 프로젝트 안 리소스 전부 접근 GCP는 folder를 쌓을 수 있고 IAM과 조직 정책이 아래로 상속됩니다. AWS 신규 경험은 조직 정책(SCP/RCP)을 AWS가 관리하고, 팀원은 프로젝트 단위로만 추가합니다. 커졌을 때 가는 길도 다릅니다. GCP는 무료 등급 project가 계층의 최상위에 있다가, 나중에 도메인에 조직이 생기면 그 조직으로 마이그레이션할 수 있습니다. AWS는 고급 기능을 활성화하면 되는데, 마이그레이션이 필요 없는 대신 되돌릴 수 없고 spend limit이 사라집니다. 🏁 정리 project는 AWS 계정 1개 단위다. 리전은 연락처 국가로 정해지고(한국은 시드니), 기본으로 같은 프로젝트 안 리소스는 모두 접근할 수 있다. team은 이메일 초대로 추가하고, 서비스별 권한 제어 없이 프로젝트 admin이 된다. 재무와 관리는 소유자만 한다. AWS는 설정 작업 없이 기본값으로 바로 시작하게 하는 기능이라고 설명하고, 코딩 에이전트로 배포하는 흐름을 같이 제공한다. GCP project는 계층의 기본 단위이고 AWS project는 계정 1개다. 커지면 GCP는 조직으로 마이그레이션하고, AWS는 되돌릴 수 없는 고급 기능 활성화로 넘어간다. 혼자 또는 소규모 팀이 빠르게 시작하거나 팀 프로젝트로 써볼 때 쓰면 좋을 것 같습니다. 이메일 초대와 프로젝트 단위 공유가 간단하고, 유료 플랜에서는 spend limit으로 비용 상한도 걸 수 있기 때문입니다. 하지만 공유한 팀원은 모두 프로젝트 admin이라 서비스별 권한을 나눌 수 없고, 리전이 연락처 국가로 정해지고(한국은 시드니), 140개 서비스는 고급 기능을 활성화해야 쓸 수 있기 때문에 역할을 나눠야 하거나 서울 리전이 필요한 팀에는 한계점이 명확합니다.
Raspberry Pi를 모니터와 키보드 없이 사용하려면 SSH(Secure Shell) 를 이용하는 것이 가장 편리하다. SSH를 사용하면 Mac의 터미널에서 Raspberry Pi에 접속하여 다음과 같은 작업을 할 수 있다. Raspberry Pi 파일 관리 Python/C 프로그램 실행 패키지 설치 Git 사용 네트워크 확인 시스템 상태 확인 Raspberry Pi 재부팅 Raspberry Pi 안전 종료 특히 Raspberry Pi를 서버나 임베디드 프로젝트용으로 사용할 경우 SSH는 거의 필수적으로 사용하게 된다. 1. SSH란? SSH는 Secure Shell 의 약자로, 네트워크를 통해 다른 컴퓨터의 터미널에 안전하게 접속하기 위한 프로토콜이다. 구조는 다음과 같다. Mac │ │ SSH │ ▼ Wi-Fi / Ethernet │ ▼ Raspberry Pi 즉, Mac의 Terminal ↓ SSH 연결 ↓ Raspberry Pi의 Linux Terminal 형태로 Raspberry Pi를 원격으로 조작하게 된다. Raspberry Pi에 모니터, 키보드, 마우스를 연결하지 않고 사용하는 방식을 흔히 Headless Setup 이라고 한다. 2. Raspberry Pi에서 SSH 활성화 SSH를 사용하려면 먼저 Raspberry Pi에서 SSH 기능이 활성화되어 있어야 한다. 방법 1. Raspberry Pi Imager에서 미리 설정 Raspberry Pi OS를 microSD 카드에 설치할 때 Raspberry Pi Imager에서 다음 항목을 미리 설정할 수 있다. Hostname Username Password Wi-Fi SSH SSH를 사용할 예정이라면 OS를 설치할 때 미리 활성화해 두는 것이 가장 편하다. 최근 Raspberry Pi OS에서는 예전처럼 기본 사용자 이름이 무조건 pi 인 것이 아니라 설치 과정에서 직접 생성한 사용자 이름 을 사용한다. 예를 들어 사용자 이름을 minwoo 로 만들었다면 이후 SSH 접속도 ssh minwoo@raspberrypi.local 과 같이 해야 한다. Raspberry Pi Imager에서는 Headless 환경에 필요한 네트워크, 사용자 계정, SSH 등을 미리 설정할 수 있다. 3. 이미 Raspberry Pi가 켜져 있다면 SSH 활성화 Raspberry Pi 터미널에서 다음 명령어를 실행한다. sudo raspi-config 설정 화면에서 SSH를 활성화한다. 또는 Raspberry Pi Desktop을 사용하고 있다면 설정 메뉴에서도 SSH를 활성화할 수 있다. 4. Mac에서 Raspberry Pi SSH 접속하기 macOS에는 SSH 프로그램이 기본으로 설치되어 있기 때문에 별도의 프로그램을 설치할 필요가 없다. Mac에서 Terminal을 실행한다. 응용 프로그램 → 유틸리티 → 터미널 또는 Spotlight에서 Terminal 을 검색하면 된다. 5. 가장 기본적인 SSH 접속 명령어 기본 형식은 다음과 같다. ssh 사용자이름@호스트이름 예를 들어 Raspberry Pi의 hostname이 raspberrypi 이고 사용자 이름이 minwoo 라면 ssh minwoo@raspberrypi.local 을 입력한다. 예: ssh minwoo@raspberrypi.local 그다음 Raspberry Pi 계정의 비밀번호를 입력한다. 비밀번호를 입력해도 터미널 화면에는 * 등의 문자가 표시되지 않는다. 아무것도 입력되지 않는 것처럼 보여도 정상이다. 비밀번호 입력 후 Enter 를 누르면 된다. 6. 처음 SSH 접속할 때 나오는 메시지 처음 연결하면 다음과 비슷한 메시지가 나타날 수 있다. The authenticity of host 'raspberrypi.local' can't be established. Are you sure you want to continue connecting (yes/no/[fingerprint])? 처음 접속한 자신의 Raspberry Pi가 맞다면 yes 를 입력한다. 그러면 Mac의 SSH known hosts 목록에 해당 Raspberry Pi가 등록된다. 이후부터는 동일한 메시지가 일반적으로 나타나지 않는다. 7. IP 주소로 SSH 접속하기 raspberrypi.local 이 제대로 동작하지 않는다면 IP 주소를 이용할 수도 있다. 예를 들어 Raspberry Pi의 IP가 192.168.0.15 라면 ssh minwoo@192.168.0.15 과 같이 접속한다. 8. Raspberry Pi IP 주소 확인 Raspberry Pi 터미널에서 다음 명령어를 실행한다. hostname -I 예: 192.168.0.15 그러면 Mac에서 ssh minwoo@192.168.0.15 로 접속할 수 있다. Raspberry Pi 공식 문서에서도 로컬 IP 확인 명령어로 hostname -I 를 안내하고 있다. 9. SSH 접속 여부 확인하기 SSH 접속에 성공하면 터미널 앞부분이 바뀐다. Mac에서는 예를 들어 minwoo@MacBook ~ % 처럼 표시될 수 있다. SSH 접속 후에는 minwoo@raspberrypi:~ $ 처럼 표시된다. 즉, minwoo@MacBook 이라면 Mac에서 명령을 실행 중 이고, minwoo@raspberrypi 라면 Raspberry Pi에서 명령을 실행 중 이다. 이 부분은 처음 SSH를 사용할 때 반드시 구분하는 것이 좋다. 10. 현재 사용자 확인 whoami 예: minwoo 현재 로그인되어 있는 Linux 사용자를 확인한다. 11. 현재 위치 확인 pwd pwd 는 Print Working Directory 의 약자이다. 예: /home/minwoo 현재 터미널이 어느 디렉터리에 있는지 확인할 수 있다. 12. 파일과 폴더 목록 확인 ls 자세히 보려면 ls -l 숨김 파일까지 보려면 ls -a 둘을 같이 사용할 수도 있다. ls -la 예: Documents Downloads BioLoop-Pi Desktop 13. 폴더 이동 기본 형식: cd 폴더이름 예를 들어 cd BioLoop-Pi 상위 폴더로 이동: cd .. 홈 디렉터리로 이동: cd ~ 또는 단순히 cd 라고 입력해도 된다. 14. 폴더 만들기 mkdir 폴더이름 예: mkdir projects 프로젝트 폴더를 만든다면 mkdir BioLoop-Pi 15. 파일 만들기 빈 파일 생성: touch test.txt 예: touch README.md 16. 텍스트 파일 내용 확인 cat 파일이름 예: cat README.md 17. 터미널에서 파일 수정하기 Raspberry Pi OS에서는 nano 를 이용하면 간단하게 파일을 수정할 수 있다. nano README.md nano에서 저장: Ctrl + O Enter를 누른 후, nano 종료: Ctrl + X 18. 파일 복사 cp 원본 대상 예: cp test.txt test_backup.txt 19. 파일 이동 또는 이름 변경 mv 기존이름 새이름 예: mv test.txt sensor_test.txt mv 는 파일을 다른 폴더로 이동할 때도 사용할 수 있다. mv sensor_test.txt ~/Documents/ 20. 파일 삭제 rm 파일이름 예: rm test.txt 폴더와 내부 파일을 모두 삭제하려면 rm -r 폴더이름 을 사용할 수 있다. 예: rm -r test_project rm 으로 삭제한 파일은 일반적인 휴지통으로 이동하지 않는다. 특히 sudo rm -rf 같은 명령어는 시스템 파일까지 삭제할 수 있으므로 정확히 이해하기 전에는 사용하지 않는 것이 좋다. 21. 화면 정리 터미널 화면이 너무 복잡해졌다면 clear 를 사용한다. 단축키로는 Ctrl + L 을 사용할 수도 있다. 22. 이전 명령어 다시 사용하기 키보드의 ↑ 방향키를 누르면 이전에 실행했던 명령어가 나타난다. 예를 들어 이전에 ssh minwoo@raspberrypi.local 을 사용했다면 위쪽 방향키를 눌러 다시 불러올 수 있다. 23. 실행 중인 명령 중단하기 프로그램이나 명령어가 계속 실행되고 있다면 Ctrl + C 를 사용한다. 예를 들어 ping google.com 을 실행하면 계속 결과가 출력되는데, Ctrl + C 를 누르면 종료된다. 24. Raspberry Pi 네트워크 확인 현재 IP 주소: hostname -I 상세 네트워크 인터페이스: ip addr 인터넷 연결 확인: ping google.com 종료: Ctrl + C 25. Raspberry Pi 시스템 정보 확인 hostname 확인: hostname 예: raspberrypi 커널 및 시스템 정보: uname -a 운영체제 정보: cat /etc/os-release 26. CPU 및 메모리 사용량 확인 실시간 시스템 상태: top 종료: q 또는 Ctrl + C 메모리 사용량: free -h 예: total used free Mem: 3.9Gi 500Mi 2.8Gi 27. 저장공간 확인 df -h microSD, SSD 등의 전체 용량과 사용량을 확인할 수 있다. 예: Filesystem Size Used Avail Use% /dev/mmcblk0p2 58G 7.2G 48G 14% 28. CPU 온도 확인 Raspberry Pi에서는 다음 명령어를 사용할 수 있다. vcgencmd measure_temp 예: temp=46.2'C 특히 Raspberry Pi 5를 사용할 경우 CPU 온도를 확인할 때 유용하다. 29. 시스템 패키지 목록 업데이트 Raspberry Pi OS는 Debian 기반이므로 apt 패키지 관리자를 사용한다. 패키지 정보를 업데이트하려면 sudo apt update 설치된 패키지를 업데이트하려면 sudo apt upgrade 보통 다음과 같이 순서대로 실행한다. sudo apt update sudo apt upgrade 30. sudo 란? Linux 명령어를 사용하다 보면 자주 등장하는 것이 sudo 이다. sudo 는 관리자 권한이 필요한 명령을 실행할 때 사용한다. 예: sudo apt update sudo reboot sudo poweroff 일반 사용자 권한으로 시스템 전체 설정을 마음대로 변경하지 못하도록 하고, 필요한 경우에만 관리자 권한을 부여하는 방식이다. 31. Mac에서 Raspberry Pi로 파일 보내기 SSH와 함께 scp 명령어를 사용하면 파일을 전송할 수 있다. 이 명령은 Raspberry Pi SSH 세션이 아니라 Mac 터미널에서 실행한다. 기본 형식: scp 파일 사용자@RaspberryPi:경로 예: scp test.py minwoo@raspberrypi.local:~/ 그러면 Mac의 test.py 가 Raspberry Pi의 홈 디렉터리로 전송된다. 32. Raspberry Pi에서 Mac으로 파일 가져오기 반대로 Raspberry Pi의 파일을 Mac으로 가져올 수도 있다. Mac 터미널에서: scp minwoo@raspberrypi.local:~/test.py . 마지막의 . 은 현재 Mac 터미널이 위치한 폴더 를 의미한다. 33. SSH 연결만 종료하기 Raspberry Pi 작업을 마쳤지만 Raspberry Pi 자체는 계속 켜두고 싶다면 exit 을 입력한다. 또는 Ctrl + D 를 사용할 수도 있다. 그러면 minwoo@raspberrypi:~ $ 에서 minwoo@MacBook ~ % 으로 돌아온다. 중요한 점은 exit 은 SSH 연결만 종료하는 명령어 라는 것이다. Raspberry Pi의 전원은 꺼지지 않는다. exit ↓ SSH 연결 종료 ↓ Mac으로 돌아옴 ↓ Raspberry Pi는 계속 실행 중 34. Raspberry Pi 재부팅 Raspberry Pi를 재부팅하려면 SSH 접속 상태에서 sudo reboot 를 입력한다. 그러면 SSH 연결이 자동으로 끊어진다. Mac │ │ sudo reboot ▼ Raspberry Pi 재부팅 │ ▼ SSH 연결 종료 잠시 후 다시 ssh minwoo@raspberrypi.local 로 접속하면 된다. Raspberry Pi 공식 문서에서도 명령줄 재부팅 명령으로 sudo reboot 를 안내한다. 35. Raspberry Pi 안전하게 종료하기 Raspberry Pi는 일반 컴퓨터처럼 운영체제를 사용한다. 따라서 전원 케이블을 바로 뽑으면 안 된다. microSD나 SSD에 데이터를 기록하고 있는 순간 전원이 끊기면 파일 손상 파일 시스템 오류 프로젝트 데이터 손실 심한 경우 OS 부팅 문제 가 발생할 수 있다. 따라서 전원을 제거하기 전에 반드시 Raspberry Pi OS를 먼저 종료해야 한다. 가장 간단한 명령어는 sudo poweroff 이다. Raspberry Pi 공식 문서에서도 명령줄에서 안전하게 종료하는 방법으로 sudo poweroff 를 안내한다. 36. Mac에서 SSH로 Raspberry Pi를 안전하게 끄는 순서 가장 중요한 실제 사용 순서는 다음과 같다. 먼저 Mac에서 접속한다. ssh minwoo@raspberrypi.local 작업을 진행한다. cd BioLoop-Pi python3 test.py 모든 작업을 마친 후 Raspberry Pi를 완전히 끌 예정이라면 sudo poweroff 를 실행한다. 그러면 SSH 연결이 끊어진다. 이후 Raspberry Pi의 디스크 접근과 시스템 종료가 완료된 것을 확인한 다음 전원을 제거한다. 전체 흐름은 다음과 같다. Mac Terminal │ │ ssh ▼ Raspberry Pi │ │ 작업 ▼ sudo poweroff │ ▼ Linux 정상 종료 │ ▼ SSH 연결 종료 │ ▼ Raspberry Pi 종료 확인 │ ▼ 전원 제거 37. exit 과 poweroff 의 차이 처음 Raspberry Pi를 사용할 때 가장 헷갈리기 쉬운 부분이다. SSH만 종료 exit 결과: SSH 연결 종료 Raspberry Pi 계속 실행 Raspberry Pi 종료 sudo poweroff 결과: Raspberry Pi OS 종료 SSH 연결 종료 Raspberry Pi 정지 정리하면 다음과 같다. 명령어 SSH Raspberry Pi exit 종료 계속 켜짐 Ctrl + D 종료 계속 켜짐 sudo reboot 끊김 재부팅 sudo poweroff 끊김 종료 38. shutdown 명령어 사용하기 Linux에서는 shutdown 명령어도 사용할 수 있다. 즉시 종료: sudo shutdown -h now 5분 후 종료: sudo shutdown -h +5 예약된 shutdown 취소: sudo shutdown -c 개인 Raspberry Pi 프로젝트에서는 보통 sudo poweroff 만 기억해도 충분하다. 39. Raspberry Pi 5의 전원 버튼 Raspberry Pi 5에는 이전 Raspberry Pi 모델과 달리 전원 버튼이 있다. Raspberry Pi OS에서는 전원 버튼을 이용한 종료 기능도 제공한다. 하지만 SSH로 작업하는 환경에서는 명령어로 sudo poweroff 를 실행하여 운영체제를 정상적으로 종료하는 습관을 들이는 것이 좋다. Raspberry Pi 5는 종료 후 저전력 soft-off 상태로 들어갈 수 있으며, 이 상태에서는 CPU와 대부분의 시스템 동작이 중지되지만 일부 전원 레일은 계속 활성화될 수 있다. 40. Raspberry Pi의 전원을 바로 뽑으면 안 되는 이유 Raspberry Pi는 microSD나 SSD에 지속적으로 데이터를 기록할 수 있다. 예를 들어 다음과 같은 작업이 백그라운드에서 발생할 수 있다. 로그 기록 파일 저장 패키지 업데이트 캐시 기록 프로그램 데이터 저장 이때 전원을 갑자기 제거하면 저장장치의 데이터가 완전히 기록되지 못할 수 있다. 따라서 항상 sudo poweroff 실행 후 종료가 완료된 것을 확인한 다음 전원을 제거하는 것이 좋다. 41. SSH 접속이 안 될 때 확인할 것 1. Raspberry Pi가 켜져 있는가? 먼저 전원이 정상적으로 공급되고 있는지 확인한다. 2. Mac과 Raspberry Pi가 같은 네트워크인가? 예를 들어 Mac ───── Home Wi-Fi │ Raspberry Pi ──┘ 같은 LAN에 연결되어 있어야 한다. 3. SSH가 활성화되어 있는가? Raspberry Pi에서 sudo raspi-config 를 통해 SSH가 활성화되어 있는지 확인한다. 4. hostname이 맞는가? hostname 으로 확인할 수 있다. 예: raspberrypi 이라면 ssh minwoo@raspberrypi.local 5. IP 주소로 접속해보기 Raspberry Pi에서 hostname -I 예: 192.168.0.15 Mac에서 ssh minwoo@192.168.0.15 42. Host key 관련 오류가 발생하는 경우 Raspberry Pi OS를 새로 설치한 뒤 SSH 접속을 시도하면 Mac에 예전 Raspberry Pi의 SSH 정보가 남아 있어 다음과 비슷한 오류가 나타날 수 있다. WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! Raspberry Pi를 직접 재설치해서 SSH key가 변경된 것이 확실하다면 Mac에서 기존 정보를 제거할 수 있다. hostname을 사용하는 경우: ssh-keygen -R raspberrypi.local IP를 사용하는 경우: ssh-keygen -R 192.168.0.15 그다음 다시 접속한다. ssh minwoo@raspberrypi.local 단, 자신이 Raspberry Pi를 재설치한 적이 없는데 갑자기 이 경고가 나타났다면 무조건 삭제하기 전에 접속 대상이 맞는지 확인하는 것이 좋다. 43. SSH 보안을 더 강화하고 싶다면 기본적으로는 사용자 이름 + 비밀번호 방식으로 SSH에 접속할 수 있다. 조금 더 익숙해진 이후에는 SSH Key를 이용하는 방법을 사용할 수 있다. Mac에서: ssh-keygen -t ed25519 SSH Key 방식은 비밀번호 방식보다 자동화하기 편하며 서버 프로젝트에서도 많이 사용된다. Raspberry Pi 공식 보안 문서에서도 SSH 보안을 강화하는 방법으로 key 기반 인증을 안내하고 있다. 처음 Raspberry Pi를 공부하는 단계에서는 우선 SSH 접속 → Linux 명령어 → 프로젝트 실행 → 안전 종료 흐름에 익숙해진 뒤 SSH Key 설정을 진행해도 충분하다. 44. 처음에는 이것만 기억해도 된다 실제로 처음 Raspberry Pi를 사용할 때 가장 자주 사용하는 명령어만 추리면 다음 정도이다. # Raspberry Pi SSH 접속 ssh minwoo@raspberrypi.local # 현재 위치 pwd # 파일 목록 ls # 폴더 이동 cd 폴더이름 # 상위 폴더 이동 cd .. # 홈으로 이동 cd ~ # 폴더 생성 mkdir 폴더이름 # 파일 내용 확인 cat 파일이름 # 파일 수정 nano 파일이름 # IP 주소 확인 hostname -I # 저장공간 확인 df -h # 메모리 확인 free -h # 패키지 정보 업데이트 sudo apt update # 패키지 업데이트 sudo apt upgrade # 재부팅 sudo reboot # Raspberry Pi 안전 종료 sudo poweroff # SSH 연결만 종료 exit 45. Raspberry Pi를 사용할 때의 기본 루틴 앞으로 프로젝트를 진행할 때는 다음 흐름을 습관처럼 사용하면 된다. 1. Raspberry Pi 전원 연결 Raspberry Pi ON ↓ 2. Mac Terminal 실행 ↓ 3. SSH 접속 ssh minwoo@raspberrypi.local ↓ 4. 프로젝트 폴더 이동 cd BioLoop-Pi ↓ 5. 프로젝트 상태 확인 ls 또는 Git 프로젝트라면 git status ↓ 6. 코드 작성 및 실행 예: nano test.py python3 test.py ↓ 7. Git에 기록 git status git add . git commit -m "Add sensor test" git push ↓ 8. 계속 Raspberry Pi를 켜둘 경우 exit ↓ 9. Raspberry Pi까지 끌 경우 sudo poweroff ↓ 10. 종료 완료 확인 후 전원 제거 이 흐름에 익숙해지면 Raspberry Pi를 모니터 없이도 대부분의 개발 작업에 사용할 수 있다. 46. 핵심 정리 Raspberry Pi를 Mac과 SSH로
배경 지식 먼저 메시징 시스템은 각자 독립적인 구성요소가 네트워크를 통해 협력하여 하나의 목표를 이루도록 설계된 시스템(분산시스템)에서 네트워크에 속하는 어댑터이다. 분산 시스템에서 주로 사용하는 메시지 패턴은 발행/구독, 분산 큐 시스템이다. 이는 서비스간 느슨한 연결을 가능하게 해서 시스템의 확장성과 안정성을 높이는데 중점을 둔다. 분산 큐 분산큐란? 시스템내 다양한 구성요소가 메시지를 안정적으로 처리하고 전달할수 있게한다. 그럼 이걸 쓰면 이점은 뭘까? 부하관리, 구성요소 간 결합도 낮추기, 신뢰성과 일관성 이다. 지금은 러프하게 말하지만 사실 부하관리 신뢰성과 일관성을 지키기 위해서는 애플리케이션의 요구에 따라 계속 바뀌기 때문에 3가지를 반드시 인지하고 정말 신경써서 구현해야한다. 그림과 같이 메시지 큐를 설계한다고 고려할때 어떤걸 고려해야할까? 메시지 순서보장: at-leat-once, exactly-once 애플리케이션 요구에 따라 데이터 영속성: 메시지를 시스템에 얼마나 오래 저장할지 보안: @valid & 메시지 무결성을 보호 메시지 형식 설계: 메시지 크기 데이터를 직렬화 또는 역직렬화 추적 및 디버깅 메타데이터 장애 허용성: 구성요소에 장애가 생기면 이를 처리. retry && DLQ(DLT) 지금은 기반 이해 지식을 간단히 정리 한것. 나중에 다시 한번 문서화 시킬때 자세히 해보자
AI 토큰으로 식물 키우는 맥 메뉴바 앱을 만들었습니다 요즘 AI 토큰으로 뭔가 키우는 앱이 종종 보이길래, 저도 하나 만들어봤습니다. TokenPlant — Claude Code나 Codex를 쓰면서 나온 토큰이 물 이 돼서 식물이 자랍니다. 아무것도 안 눌러도 됩니다. 개발하다 보면 메뉴바에서 알아서 자라 있어요. GitHub: https://github.com/seoyeon1123/tokenplant macOS 14 (Sonoma) 이상 어떻게 동작하나 맥에 쌓이는 로컬 Claude Code / Codex 로그 ( ~/.claude/projects , ~/.codex/sessions )를 30초마다 읽습니다. 서버는 없습니다. 어디로도 전송하지 않고요. 읽은 토큰은 두 갈래로 동시에 들어갑니다. 들어가는 곳 하는 일 가중 환산 mL 화분 그냥 둬도 자란다 원시 토큰 지갑 상점에서 물·거름·씨앗을 산다 여기가 설계에서 제일 고민한 부분인데, 둘은 서로 빼앗지 않습니다. 같은 토큰이 양쪽에 다 들어가요. 이렇게 안 하면 "지갑을 쓰면 식물이 느려지나?"라는 질문이 생기고, 그 순간 상점을 쓰는 게 손해처럼 느껴집니다. 실제로 제 친구가 처음 써보고 똑같이 물어봤어요. 두 갈래로 나눠두면 상점은 가속 장치 가 되지 비용 이 되지 않습니다. 식물은 10단계로 자랍니다 씨앗 → 발아 → 떡잎 → ... → 거목. 종류는 15가지고 희귀도가 있습니다. 문턱을 앞쪽에 몰아놨습니다. 첫 이틀 안에 레벨업이 5번 일어나요. 처음 깔고 아무 변화가 없으면 그냥 지우거든요. 대신 뒤로 갈수록 느려집니다. 다 자라면 정원으로 옮겨집니다 거목이 되면 정원으로 이식되고, 화분에는 새 씨앗이 심깁니다. 정원은 그루 수에 따라 넓어져요. 창가 → 베란다 → 작은 화단 → 뒷마당 → 온실 → 수목원 → 비밀의 숲 배경은 그림이 아니라 요소 플래그 조합 으로 그립니다. 하늘·잔디·울타리·유리·연못을 켜고 끄는 식이에요. 7티어 × 계절 3종을 그림으로 다 그리면 21장인데, 이렇게 하면 팔레트만 갈아끼우면 됩니다. 뒷줄 나무는 색을 하늘색 쪽으로 22% 물려서 거리감을 줬습니다. 픽셀아트는 축소하면 깨지니까 크기가 아니라 색으로 원근을 만들었어요. 종은 팔레트 스왑입니다 실루엣은 3개만 그렸습니다 — 둥근 것 / 길쭉한 것 / 낮게 퍼진 것. 종 구분은 잎 색 팔레트로 합니다. 실루엣 3 × 팔레트 N = 종 3N개 종을 늘릴 때 그림을 새로 안 그려도 되고, 반짝(shiny) 변종도 금색 팔레트 하나로 끝납니다. 상점과 장식 지갑에 쌓인 토큰으로 물·거름·영양제·씨앗·장식을 삽니다. 장식 12종 중 3개는 직접 사고 9개는 뽑기로만 나와요. 다 직접 살 수 있게 하면 비싼 순서대로 사는 목록이 되는데, 그건 재미가 아니라 숙제라서요. 정원에 놓인 것들은 움직입니다. 바람개비가 돌고, 고양이가 꼬리를 흔들고, 모이통이나 물받이를 놓으면 새가 날아와서 앉았다가 모이를 쪼아 먹습니다. 오버레이를 얹지 않고 5fps로 격자를 통째로 다시 조립하는 방식입니다. 오버레이는 배경과 좌표·배율을 맞춰야 해서 정렬 버그가 나고, 드래그와 PNG 내보내기 경로를 따로 만들어야 하거든요. 움직일 게 없거나 창이 뒤에 있으면 아예 안 돕니다. 기술 스택 Swift 6 / SwiftUI / SwiftPM (Xcode 프로젝트 없음) MenuBarExtra(.window) — 메뉴바 팝오버 @Observable + @MainActor 배포: GitHub Releases + 자체 Homebrew tap + Sparkle 설치 brew tap seoyeon1123/tap brew install --cask tokenplant 한 가지 주의할 게 있는데, 터미널에서 쓰는 CLI 사용량만 셉니다. 웹이나 데스크톱 앱으로 쓰신 건 맥에 기록이 안 남아서 셀 수 없어요. (이걸 모르고 "왜 안 자라지" 하면서 하루를 날린 사람이 접니다.) 부족한 게 많습니다. 써보시고 이상한 점이나 "이런 거 있으면 재밌겠다" 싶은 아이디어 있으면 이슈로 남겨주세요. PR도 환영합니다. 감사합니다 🧡 https://github.com/seoyeon1123/tokenplant
th scope로 되는지, headers/id까지 가야 하는지 스크린리더는 표를 통째로 보지 못하고 칸 하나씩 읽습니다. 그래서 숫자 칸에 갔을 때 "이 숫자가 무슨 숫자인지" 제목을 같이 읽어줘야 하는데, 어느 제목 칸(th)이 어느 숫자 칸(td)의 제목인지를 HTML이 알려줘야 합니다. 그걸 알려주는 방법이 둘이고, 그게 scope와 headers/id입니다. 1단 표: scope로 충분 | 기간 | 금리 | | 6개월 | 3.0% | | 12개월 | 3.2% | <th scope="col">기간</th> <th scope="col">금리</th> <th scope="row">6개월</th> <td>3.0%</td> scope는 "나는 이 열(col)의 제목이다", "나는 이 행(row)의 제목이다"라고 방향만 알려줍니다. 리더가 3.0%에 가면 위로 올라가 "금리", 왼쪽으로 가서 "6개월"을 찾아 "6개월, 금리, 3.0%"라고 읽습니다. 제목이 한 줄이면 이걸로 끝입니다. 2단 표: 제목이 두 줄이라 scope가 모자람 광주은행 금리표가 이랬습니다. 기간 아래에 기본·우대·최고가 또 있습니다. | | 6개월 | 12개월 | | | 기본 | 우대 | 최고 | 기본 | 우대 | 최고 | | 100만원 | 3.0 | 3.3 | 3.5 | 3.2 | 3.5 | 3.8 | 3.5에 갔을 때 사람 눈은 "6개월의 최고금리"라고 바로 압니다. 그런데 scope만 있으면 리더가 바로 위 제목 "최고"만 잡고, 그 위에 여러 칸을 합쳐서 걸쳐 있는 "6개월"은 못 붙이는 경우가 있습니다. 그러면 "최고, 3.5"만 들립니다. 6개월 최고인지 12개월 최고인지 모르는 겁니다. 심사에서 "열 제목이 읽히지 않음"으로 잡힌 게 이겁니다. headers/id: 칸마다 제목을 직접 적어줌 <th id="m6" colspan="3">6개월</th> <th id="max6">최고</th> <th id="r100">100만원</th> <td headers="r100 m6 max6">3.5%</td> 제목 칸마다 이름표(id)를 붙이고, 숫자 칸에 "내 제목은 이 셋이다"(headers)라고 직접 적습니다. 그러면 리더가 방향을 찾아갈 필요 없이 "100만원, 6개월, 최고, 3.5%"라고 정확히 읽습니다. 대신 칸이 열두 개면 열두 번 적어야 하고, 하나라도 틀리면 엉뚱한 제목이 읽힙니다. 그래서 1단 표에는 안 쓰고, 2단처럼 scope로 안 될 때만 씁니다. 면접에서 말할 땐 이 정도면 됩니다. "scope는 제목 칸에 '이건 이 열 제목'이라고 방향만 알려주는 거고, headers/id는 숫자 칸마다 '내 제목은 이것들'이라고 직접 적어주는 겁니다. 금리표는 기간 아래 기본·우대·최고가 또 있는 2단이라, 방향만으로는 위쪽 제목인 기간이 안 읽혀서 headers/id까지 갔습니다."
ASG (Auto Scaling Group) Launch Template 기반으로 EC2를 자동 생성/삭제/관리하는 서비스 Launch Template이란? ASG가 EC2를 만들 때 사용하는 설계도 어떤 OS를 사용할지(Amazon Linux 2023) 어떤 사양인지(t4g.small) 어떤 트래픽을 허용할지(보안 그룹) 어떤 AWS 권한을 가질지(IAM) EC2 시작 시 실행할 스크립트(User Data) 핵심 설정 Desired: 지금 유지하고 싶은 인스턴스 수(인스턴스 장애 시 새 EC2 생성) Min: 최소 인스턴스 수(트래픽 줄면 Min 수준까지 인스턴스 제거) Max: 최대 인스턴스 수(부하 발생 시 Max 범위 내에서 자원 추가)
2026년 9월 15일 TypeSafe AI는 Jev 라는 새로운 AI 모델을 공개했다. Jev는 ChatGPT나 Claude처럼 텍스트를 생성하는 LLM이 아니다. TypeSafe는 Jev를 첫 번째 System One Model 이라고 정의한다. 출처: TypeSafe AI Documentation - Introduction 핵심 아이디어는 간단하다. LLM이 자연어를 생성하는 모델이라면, Jev는 소프트웨어가 바로 사용할 수 있는 결정을 내리는 모델이다. TypeSafe는 Jev를 다음과 같이 표현한다. unstructured state in, typed probabilistic decisions out. 즉, 자연어나 구조화된 상태(state)를 입력하면 타입이 정의된 결정과 그 확률 을 반환한다. 이 글에서는 Jev가 기존 LLM과 무엇이 다른지 살펴보고, Python SDK를 사용해 실제로 호출하는 방법까지 정리한다. 1. 왜 Jev가 등장했을까? LLM은 기본적으로 text generation model 이다. 예를 들어 고객 문의를 분류한다고 해보자. 사용자: 결제가 두 번 됐어요. 환불해주세요. 질문: 이 문의를 담당해야 하는 부서를 JSON으로 반환해줘. 가능한 부서: billing technical sales LLM은 내부적으로 다음과 같이 토큰을 하나씩 생성한다. { "department": "billing" } 이 방식은 사람이 읽는 답변을 만들 때는 매우 유용하다. 하지만 소프트웨어의 의사결정 모듈로 사용하면 약간 어색한 부분이 생긴다. Text ↓ LLM ↓ Generated String ↓ JSON Parsing ↓ Schema Validation ↓ Application Logic 즉 우리가 실제로 원하는 것은 billing 이라는 결정 인데, 모델은 우선 문자열을 생성하고 우리는 다시 그 문자열을 프로그램이 사용할 수 있는 데이터로 변환해야 한다. TypeSafe는 이를 LLM과 software automation 사이의 interface mismatch 라고 본다. Jev는 이 과정을 다음처럼 바꾼다. State ↓ Jev ↓ Typed Decision ↓ Application Logic 텍스트를 생성한 뒤 파싱하는 것이 아니라 처음부터 구조화된 decision 을 반환하는 것이다. 2. Jev는 무엇인가? Jev는 TypeSafe AI가 개발한 첫 번째 System One Model 이다. System One이라는 이름은 Daniel Kahneman의 Thinking, Fast and Slow 에서 등장하는 System 1에서 가져왔다. 빠르고 직관적인 판단을 담당하는 인간의 사고 방식을 모델 설계 철학에 차용한 것이다. Jev가 목표로 하는 것은 다음과 같은 문제다. classification routing scoring ranking verification guardrail branching 예를 들어 고객 문의가 들어왔을 때 다음 질문을 동시에 판단할 수 있다. 어느 부서가 담당해야 하는가? → billing / technical / sales 고객은 얼마나 화가 났는가? → calm / frustrated / angry 긴급한 요청인가? → probability 0~1 여기서 중요한 점은 Jev가 답변을 문장으로 생성하지 않는다는 것 이다. 대신 개발자가 미리 정의한 output space에서 결과를 선택한다. 3. LLM과 Jev의 가장 큰 차이 두 모델의 차이를 간단히 정리하면 다음과 같다. LLM Jev 주요 목적 Text Generation Decision 출력 String / Tokens Typed Value 출력 방식 Autoregressive Parallel Parsing 필요할 수 있음 불필요 후보 공간 사실상 무제한 개발자가 정의 Probability 일반적으로 직접 노출하지 않음 기본 제공 Confidence 별도 추정 필요 Choice/Score에서 제공 적합한 작업 대화, 글쓰기, 코드 생성, reasoning classification, routing, scoring, verification LLM은 다음 토큰을 반복적으로 예측한다. P(x_t | x_1, ..., x_{t-1}) 따라서 문장이 길어질수록 여러 decoding step이 필요하다. 반면 TypeSafe에 따르면 Jev는 여러 decision을 parallel sampling 방식으로 처리하며, 여러 질문을 하나의 요청에 넣더라도 응답시간 증가가 상대적으로 작도록 설계됐다. TypeSafe가 공개한 자료에서는 System One task 기준으로 Jev의 응답 시간이 약 70–500ms 수준이라고 설명한다. 다만 이는 TypeSafe 측 측정값이며 네트워크 위치, 요청 구조, 모델 버전 등에 따라 실제 latency는 달라질 수 있다. 4. Jev의 핵심 구조: State + Questions Jev API를 이해하기 위해 가장 중요한 개념은 단 두 가지다. state questions State state 는 Jev가 판단해야 하는 대상이다. 예를 들어 고객 문의라면 다음 문장이 state가 된다. I've been trying to connect my Stripe account for 3 days. The integration keeps failing and I'm losing sales. Questions questions 는 이 state를 보고 Jev가 판단해야 하는 항목이다. 예를 들어: 어느 부서가 담당해야 하는가? 고객의 frustration 정도는? 긴급한 요청인가? Jev는 이 질문들을 각각 판단한다. 공식 문서에서는 각 question이 동일한 state를 기준으로 독립적으로 평가되며 병렬로 처리된다 고 설명한다. 따라서 하나의 복잡한 질문을 만드는 것보다 판단 기준을 여러 atomic question으로 분리하는 것을 권장한다. 5. Jev의 세 가지 Primitive 현재 TypeSafe API에는 세 가지 핵심 question type이 존재한다. Choice Score Noul 각각 역할이 명확하다. Type 목적 예 Choice 여러 후보 중 하나 선택 어떤 부서로 보낼까? Score 순서가 있는 단계 평가 고객은 얼마나 화났나? Noul Yes/No 명제의 확률 환불을 요청했는가? 6. Choice: 여러 후보 중 하나 선택하기 Choice 는 전형적인 classification 문제에 해당한다. 예를 들어 고객 문의를 세 부서 중 하나로 분류한다고 해보자. from typesafe_sdk import Choice, TypeSafeClient client = TypeSafeClient() response = client.system_one( state="My payment was charged twice. I want a refund.", questions={ "department": Choice( instructions="Which team should handle this request?", criteria={ "billing": "Payment, subscription, invoice, or refund issues", "technical": "Software bugs or technical problems", "sales": "Pricing or purchasing questions", }, ) }, ) answer = response.answers["department"] print(answer.choice) print(answer.probabilities) print(answer.confidence) 결과는 개념적으로 다음과 같다. choice = "billing" probabilities = { "billing": 0.91, "technical": 0.03, "sales": 0.06 } confidence = 0.87 Choice는 단순히 가장 높은 후보만 반환하는 것이 아니다. 전체 후보의 probability distribution 도 함께 받을 수 있다. choice 는 이들 중 가장 높은 확률을 가진 option이다. 따라서 다음과 같은 로직도 만들 수 있다. if answer.confidence < 0.5: send_to_human_review() elif answer.choice == "billing": send_to_billing() elif answer.choice == "technical": send_to_technical() 이 부분이 일반적인 LLM classification과 상당히 다른 지점이다. 모델의 판단을 바로 실행하는 것이 아니라 uncertainty를 application logic에 포함할 수 있다. 7. Noul: Yes / No를 확률로 판단하기 Noul 은 조금 독특한 이름의 primitive다. 역할 자체는 간단하다. 주어진 명제가 참일 확률을 0~1 사이 값으로 반환한다. 예를 들어 다음 고객 문의가 있다고 하자. I have asked three times already. Can I please talk to a real person? 다음과 같이 질문할 수 있다. from typesafe_sdk import Noul, TypeSafeClient client = TypeSafeClient() response = client.system_one( state=""" I have asked three times already. Can I please talk to a real person? """, questions={ "needs_human": Noul( instructions="Is the customer asking for a human agent?" ) }, ) probability = response.answers["needs_human"].noul print(probability) 예를 들어 결과가 0.99 라면 P(customer wants human | state) ≈ 0.99 와 같은 의미로 사용할 수 있다. 공식 문서 예시에서도 실제 해당 문장에 대해 0.99 가 반환된다. 따라서 application에서는 threshold만 설정하면 된다. if probability > 0.9: route_to_human() else: route_to_bot() 물론 실제 production 환경에서는 0.9라는 threshold를 임의로 정하기보다 validation set을 기반으로 조정해야 한다. 특히 false positive와 false negative의 비용이 다르다면 threshold 역시 달라져야 한다. 예를 들어 safety detection이라면 false negative의 비용이 크기 때문에 threshold를 낮추는 것이 합리적일 수 있다. TypeSafe 문서 역시 threshold는 decision error의 비용에 따라 설정해야 한다 고 권장한다. 8. Score: 순서가 있는 정도를 평가하기 Choice가 categorical classification이라면 Score는 ordinal prediction 에 가깝다. 예를 들어 고객의 frustration을 평가할 수 있다. from typesafe_sdk import Score frustration = Score( instructions="How frustrated does the customer appear?", criteria=[ "Calm, simply stating the issue", "Frustrated but still civil", "Very angry or aggressive", ], ) criteria 에는 낮은 수준부터 높은 수준 순서로 description을 작성한다. 공식 문서상 Score에는 최소 2개, 최대 10개의 level을 지정할 수 있다. 결과는 예를 들어 다음과 같이 나온다. score = 1.2 probabilities = { 0: 0.05, 1: 0.70, 2: 0.25 } 여기서 재미있는 점은 Score 값이 반드시 정수일 필요가 없다는 것이다. 두 level 사이의 확률 분포를 기반으로 중간값을 표현할 수도 있다. 9. 여러 질문을 한 번에 요청하기 Jev의 중요한 특징 중 하나는 여러 decision을 하나의 request에 넣을 수 있다는 것이다. 예를 들어 고객 문의 분석 시스템을 다음처럼 구성할 수 있다. from typesafe_sdk import Choice, Noul, Score, TypeSafeClient client = TypeSafeClient() ticket = """ I've been trying to connect my Stripe account for three days. The integration keeps failing and I'm losing sales. Please help ASAP. """ response = client.system_one( state=ticket, questions={ "department": Choice( instructions="Which team should handle this?", criteria={ "billing": "Payment or subscription issues", "technical": "Bugs or integration problems", "sales": "Pricing or purchasing questions", }, ), "frustration": Score( instructions="How frustrated does the customer appear?", criteria=[ "Calm", "Frustrated", "Very angry", ], ), "urgent": Noul( instructions="Does this message express urgency?" ), }, ) 이렇게 하면 department frustration urgent 세 가지 판단을 한 번의 요청으로 얻는다. 공식 문서는 여러 question을 한 request에 넣는 것을 권장한다. 각 question은 같은 state에 대해 독립적으로 평가되며 병렬 처리되기 때문에, question 수를 늘리더라도 latency가 크게 증가하지 않는 것이 Jev 설계의 특징이라고 설명한다. 다만 추가 question 역시 token cost에는 반영된다. 10. Jev 시작하기 Jev를 사용하는 방법은 크게 두 가지다. 가볍게 테스트만 해보고 싶다면 TypeSafe Playground 를 사용할 수 있고, 실제 코드에서 사용하려면 API Key를 발급받아 SDK 또는 REST API로 호출 해야 한다. 공식 문서 역시 먼저 Playground에서 Jev를 시험해본 뒤 API를 사용하는 흐름을 안내한다. Step 0. Playground에서 먼저 사용해보기 처음부터 Python 코드를 작성할 필요는 없다. TypeSafe Console의 Playground에 로그인하면 state 와 question 을 직접 입력해 Jev의 결과를 확인할 수 있다. 예를 들어 state에 다음 내용을 입력한다. Hi, I've been trying to connect my Stripe account for 3 days. The integration keeps failing. I'm losing sales. Please help ASAP. 그리고 Noul question을 하나 추가한다. Does this message express urgency? 그러면 Jev가 해당 명제가 참일 확률을 반환한다. 공식 Quick Start에서는 Jev를 처음 접한다면 이 Playground 방식으로 먼저 Noul , Choice , Score 를 시험해보는 방법을 제시한다. Step 1. TypeSafe 계정 생성 및 API Key 발급 Python SDK나 REST API를 사용하려면 TypeSafe API Key가 필요하다. TypeSafe Console에 로그인한 뒤 Dashboard에서 API Key를 발급한다. 발급받은 키는 다음처럼 생긴 인증 정보이며 외부에 공개하면 안 된다. your-typesafe-api-key 특히 GitHub에 코드를 올릴 때 다음처럼 API Key를 코드에 직접 작성하는 것은 피하는 것이 좋다. # 권장하지 않음 client = TypeSafeClient( api_key="your-api-key" ) 대신 환경 변수로 관리한다. Step 2. API Key를 환경 변수에 등록하기 macOS나 Linux에서는 터미널에서 다음과 같이 설정할 수 있다. export TYPESAFE_API_KEY="your-api-key" Windows PowerShell이라면 다음과 같이 설정할 수 있다. $env:TYPESAFE_API_KEY="your-api-key" Python에서 제대로 등록됐는지 확인하고 싶다면: import os print(os.getenv("TYPESAFE_API_KEY")) 값이 출력된다면 설정된 것이다. 단, 실제 개발 환경에서는 API Key 전체를 로그에 출력하지 않는 것이 좋다. TypeSafe Python SDK의 TypeSafeClient() 는 기본적으로 TYPESAFE_API_KEY 환경 변수를 자동으로 읽는다. 따라서 이후 코드에서 API Key를 매번 전달할 필요가 없다. Step 3. Python SDK 설치 현재 공식 SDK는 Python 3.10 이상을 요구한다. pip를 사용하는 경우: pip install typesafe-sdk uv를 사용한다면: uv add typesafe-sdk Step 4. 가장 간단한 Jev 호출 설치가 끝났다면 다음 코드만으로 Jev를 호출할 수 있다. from typesafe_sdk import Noul, TypeSafeClient client = TypeSafeClient() response = client.system_one( state="The customer wants a refund because they were charged twice.", questions={ "refund_request": Noul( instructions="Is the customer requesting a refund?" ) }, ) print(response.answers["refund_request"].noul) 여기서 client = TypeSafeClient() 에 API Key가 보이지 않는 이유는 SDK가 앞서 설정한 TYPESAFE_API_KEY 환경 변수를 자동으로 읽기 때문이다. 또한 모델을 별도로 지정하지 않으면 현재 공식 문서 기준으로 기본 모델인 jev-latest 가 사용된다. 전체 과정을 정리하면 처음 사용하는 경우 다음 순서만 따르면 된다. 1. TypeSafe Console 가입 및 로그인 ↓ 2. Playground에서 Jev 테스트 ↓ 3. Dashboard에서 API Key 발급 ↓ 4. TYPESAFE_API_KEY 환경 변수 등록 ↓ 5. pip install typesafe-sdk ↓ 6. TypeSafeClient() 생성 ↓ 7. client.system_one() 호출 Playground에서 테스트할 때는 별도의 Python 환경이나 SDK가 필요하지 않다. 반면 실제 애플리케이션에서 Jev를 호출하려면 API Key를 발급받아 인증해야 한다. 공식 REST API에서도 다음과 같이 Bearer 인증을 사용한다. Authorization: Bearer <API_KEY> 공식 API endpoint는 현재 다음과 같다. POST https://api.typesafe.ai/v1/systemone 이제 API Key 설정이 끝났다면 Choice , Score , Noul 을 실제 코드에서 사용할 수 있다. 11. HTTP API로 직접 호출하기 SDK를 사용하지 않고 REST API를 직접 호출하는 것도 가능하다. endpoint는 현재 다음과 같다. POST https://api.typesafe.ai/v1/systemone Authorization header에는 Bearer token을 사용한다. 예를 들어: curl -X POST https://api.typesafe.ai/v1/systemone \ -H "Authorization: Bearer $TYPESAFE_API_KEY" \ -H "Content-Type: application/json" \ -d '{
[머신러닝] 4강 비지도학습: 군집화 (Clustering) 핵심 정리 📌 오늘의 학습 목표 지도학습(분류)과 비지도학습(군집화)의 기본 개념 차이를 구분하고, 군집화의 필요성을 이해한다. K-평균(K-Means) 군집화 알고리즘의 4단계 수행 과정과 주요 특징을 설명할 수 있다. 계층적 군집화(Hierarchical Clustering)의 두 가지 접근 방식(병합적 vs 분할적)과 덴드로그램의 역할을 파악한다. 1. 핵심 개념 요약 0. 비지도학습과 군집화(Clustering)란? 개념 및 필요성 : 입력 데이터 간의 유사성을 기준으로 데이터를 몇 개의 그룹으로 나누는 문제입니다. 목표 출력값(정답 레이블)이 없기 때문에 비지도학습(Unsupervised Learning) 에 속하며, 정답이 없거나 데이터 레이블링에 비용이 많이 드는 경우 유용하게 활용됩니다. 주요 응용 분야 : 영상 데이터 그룹핑, 영상 분할(Image Segmentation) 등에 적용됩니다. 1. K-평균 군집화 (K-Means Clustering) 개념 : 주어진 데이터 집합을 $K$개의 대표 벡터(평균)를 중심으로 $K$개의 그룹으로 묶는 대표적인 군집화 알고리즘입니다. 수행 4단계 과정 : 초기화 (Initialization) : 데이터 집합에서 임의로 $K$개의 초기 대표 벡터 $\mathbf{m}_1, \mathbf{m}_2, \dots, \mathbf{m}_K$를 선택합니다. 데이터 그룹핑 (Data Grouping) : 각 데이터 $\mathbf{x}_j$와 $K$개의 대표 벡터 간 거리 $d(\mathbf{x}_j, \mathbf{m}_k)$를 계산하고, 가장 가까운 대표 벡터를 가진 클러스터 $C_k$에 배정합니다. $$C_k = { \mathbf{x}_j \mid d(\mathbf{x}_j, \mathbf{m}_k) \le d(\mathbf{x}_j, \mathbf{m}_i), , \forall i }$$ 대표 벡터 수정 (Updating Centroids) : 새롭게 형성된 클러스터에 속한 데이터들의 평균을 구해 대표 벡터를 다시 갱신합니다. 반복 여부 결정 (Iteration) : 대표 벡터의 변화가 없거나 수렴할 때까지 2단계와 3단계를 반복합니다. 특징 및 한계 : 반복 과정을 거치며 지역 극소점(Local Minima) 에 도달하는 것을 보장합니다. 초기 대표 벡터 위치 설정에 매우 의존적이며, 적절한 $K$값을 사전에 설정하기 어렵다는 한계가 있습니다. 2. 계층적 군집화 (Hierarchical Clustering) 개념 : 큰 군집이 작은 군집을 포함하는 형태로 계층(트리 구조)을 이루며 군집화를 수행하는 방식입니다. 접근 방식 : 병합적 방법 (Bottom-Up) : 각 데이터를 독립된 개별 군집으로 시작하여, 가장 가까운 군집 쌍을 순차적으로 합쳐 하나의 큰 군집을 만드는 실용적인 방식입니다. 분할적 방법 (Top-Down) : 전체 데이터를 하나의 큰 군집으로 시작하여 점차 쪼개어 나가는 방식으로, 계산량이 많아 다소 비실용적입니다. 군집 간 거리 계산 및 덴드로그램 : 군집 간 거리를 계산할 때 와드(Ward's) 방법 등 다양한 거리 측정 기준을 사용합니다. 계층적 군집화 결과는 트리 형태의 덴드로그램(Dendrogram) 으로 시각화되며, 클러스터 간 거리가 유지되는 지점을 잘라 적합한 군집의 수를 결정합니다. 2. 오늘 배운 내용 요약 군집화는 정답 레이블이 없는 데이터에서 데이터 간 유사도를 측정하여 대표 벡터 기반(K-Means) 또는 계층 구조(Hierarchical) 형태로 의미 있는 그룹을 형성하는 비지도학습 기법입니다.
블로그에 구독 메일을 붙이면서 발송은 Resend라는 서비스에 맡겼습니다. 구독자에게 보이는 발신 주소는 newsletter@wonkooklee.com 이지만, 메일을 실제로 전달하는 것은 Resend의 서버입니다. 저는 메일 서버를 운영하지 않습니다. 그런데도 Gmail은 이 메일이 정말 wonkooklee.com에서 왔는지 판단해야 합니다. 보낸 사람 칸에 적힌 From 은 그 근거가 되지 못합니다. 메일을 만드는 쪽이 적는 값이라, 제 도메인을 사칭하려는 사람도 자기 서버에서 똑같이 적을 수 있습니다. 그래서 수신 서버는 메일 안의 주장과 별개로, 도메인 관리자만 바꿀 수 있는 DNS 설정을 조회해 검사합니다. 글은 구독 메일 한 통을 따라가며 그 검사를 정리합니다. SPF가 확인하는 주소는 화면에 보이는 From이 아니라 배달 실패 알림을 받을 MAIL FROM 입니다 DKIM은 서명의 d= 와 s= 로 DNS에서 공개키를 찾습니다. 그래서 Zoho와 Resend가 한 도메인에 각자 키를 둘 수 있습니다 남의 도메인으로 SPF와 DKIM을 모두 통과한 사칭 메일이 DMARC에서 걸리는 이유: 인증한 도메인과 From의 정렬(alignment) SMTP는 From보다 MAIL FROM 을 먼저 보냅니다. 이 순서 때문에 SPF를 -all 로 끝내면 DKIM으로 DMARC를 통과했을 메일이 From이 도착하기도 전에 거부되고, 집계 보고서에도 남지 않을 수 있습니다 p=none 으로 시작해 집계 보고서( rua )로 놓친 발송 경로를 찾은 뒤 quarantine 이나 reject 를 검토하는 순서 실제로 조회한 wonkooklee.com의 SPF·DKIM·DMARC 설정과, 레코드만 봐서는 알 수 없는 것 Gmail에 발신자 로고를 띄우는 BIMI가 p=none 과 로고 파일만으로는 요건을 채우지 못하는 이유 SPF와 DKIM은 각자 확인해야 할 것을 제대로 확인합니다. 다만 둘 다 독자가 보는 주소까지 확인하지는 않고, 그 연결은 DMARC가 맡습니다. 그리고 DMARC를 통과했다고 메일 내용을 믿어도 되는 것은 아닙니다. 공격자가 자기 도메인으로 보낸 메일도 발신 인증은 정상적으로 통과합니다. 확인되는 것은 보낸 도메인까지이고, 본문과 링크가 안전한지는 그 밖의 문제로 남습니다. 전문은 블로그에서 이어집니다 👇 ▶ 내 도메인에서 보낸 이메일은 어떻게 진짜임을 증명할까 (전문 보기) 다른 글도 블로그에서 이어집니다 · 전체 목차 보기
백트래킹(Backtracking) 1. 백트래킹이란? 백트래킹은 여러 가지 선택지가 존재하는 상황에서 하나를 선택하고, 그 선택을 기반으로 다음 선택을 이어가며 정답을 찾는 방식이다. 탐색을 진행하다가 하나의 경로가 끝나면 이전 상태로 돌아가 다른 선택지를 다시 탐색한다. 즉, 백트래킹의 핵심은 다음과 같다. 선택 → 탐색 → 선택 취소 → 다른 선택 예를 들어 [1, 2, 3] 을 이용해 순열을 만든다고 생각하면, 1 선택 └─ 2 선택 └─ 3 선택 탐색 완료 ↓ 2 선택 취소 ↓ 3 선택 처럼 이전 선택을 취소하고 다른 경우를 다시 탐색한다. 2. 백트래킹과 DFS의 차이 DFS(Depth First Search)는 한 방향으로 최대한 깊게 들어가며 탐색하는 탐색 방식 이다. 백트래킹은 DFS를 이용해 하나의 선택을 탐색한 뒤, 탐색이 끝나면 이전 상태로 되돌아가 다른 선택을 시도하는 문제 해결 방식 이다. DFS = 한 방향으로 깊게 탐색 백트래킹 = 선택 → 깊게 탐색 → 선택 취소 → 다른 선택 DFS 역시 탐색이 끝나면 이전 노드로 돌아온다. 하지만 백트래킹에서는 단순히 탐색 위치만 돌아가는 것이 아니라, 탐색 전에 변경했던 상태까지 원상복구 한다는 점이 중요하다. 3. 가지치기(Pruning) 백트래킹 과정에서 현재 선택이 정답으로 이어질 가능성이 없다고 판단되면, 해당 경로를 더 이상 탐색하지 않을 수 있다. 이것을 가지치기(Pruning) 라고 한다. 현재 경로 ↓ 정답 가능성 없음 ↓ 더 이상 탐색하지 않음 가지치기를 사용하면 모든 경우를 끝까지 확인하지 않아도 되기 때문에 탐색 횟수를 크게 줄일 수 있다. 백트래킹과 가지치기는 같은 개념은 아니다. 백트래킹 = 탐색 후 이전 상태로 돌아가는 것 가지치기 = 가능성이 없는 경로를 탐색하지 않는 것 4. 백트래킹 알고리즘 절차 백트래킹은 일반적으로 다음과 같은 과정으로 진행된다. 상태 공간 트리를 DFS 방식으로 탐색한다. 현재 선택이 유망한지 확인한다. 유망하다면 다음 단계로 탐색한다. 유망하지 않다면 해당 경로의 탐색을 중단한다. 탐색이 끝나면 이전 상태로 돌아가 다른 선택지를 탐색한다. 여기서 유망하다 는 것은 현재 선택을 계속 이어갔을 때 정답이 될 가능성이 있다는 의미이다. 5. 대표 문제 - N-Queen N-Queen은 N × N 체스판에 N개의 퀸을 서로 공격할 수 없도록 배치하는 문제이다. 퀸은 다음 방향으로 이동할 수 있다. 같은 열 왼쪽 대각선 오른쪽 대각선 따라서 새로운 퀸을 놓을 때 이 세 방향에 다른 퀸이 존재하는지 검사해야 한다. 8-Queen N = 8 인 경우 가능한 배치의 개수는 92개 이다. # 입력 8을 줌 n = int(input()) vertical = [0] * n seven = [0] * (2 * n) five = [0] * (2 * n) count = 0 def backtracking(level): global count if level == n: count += 1 return for j in range(n): # 같은 열 확인 if vertical[j] == 1: continue # 대각선 확인 if seven[level + j] == 1 or five[level - j + n] == 1: continue vertical[j] = 1 seven[level + j] = 1 five[level - j + n] = 1 backtracking(level + 1) # 이전 상태로 복구 vertical[j] = 0 seven[level + j] = 0 five[level - j + n] = 0 backtracking(0) print(count) 출력 6. 코드에서 각 개념 구분하기 N-Queen 코드에는 완전탐색, DFS, 가지치기, 백트래킹이 모두 들어 있다. 완전탐색 for j in range(n): 현재 행에서 퀸을 놓을 수 있는 모든 열을 하나씩 확인한다. 즉, 가능한 선택지를 전부 시도한다. DFS backtracking(level + 1) 현재 행에 퀸을 놓은 뒤 다음 행으로 넘어가 깊게 탐색한다. 가지치기 if vertical[j] == 1: continue if seven[level + j] == 1 or five[level - j + n] == 1: continue 이미 다른 퀸의 공격 범위에 있는 위치라면 해당 경우는 정답이 될 수 없으므로 탐색하지 않는다. 백트래킹 vertical[j] = 0 seven[level + j] = 0 five[level - j + n] = 0 현재 선택에 대한 탐색이 끝나면 퀸을 놓기 전 상태로 원상복구한다. 이후 반복문에서 다음 위치를 선택해 다시 탐색한다.
NAT Gateway 프라이빗 서브넷 내의 인스턴스가 외부 인터넷과 통신할 수 있도록 네트워크 주소 변환(NAT)을 수행하는 클라우드 네트워킹 서비스 아웃바운드 전용: 프라이빗 서브넷 내부에서 외부 인터넷을 호출할 수는 있지만, 외부 이넡넷에서 프라이빗 서브넷 내부 인스턴스에 접속할 수는 없다. Zonal Gateway, Regional Gateway Zonal NAT Gateway 기존에 있던 방식 AZ마다 설정해주어야 함 Regional NAT Gateway 최신 버전 Region 전체를 커버하는 NAT Gateway 하나만 만들면 별다른 설정 없이도 AWS가 AZ별로 알아서 확장해줌 ECR에서 Docker Image를 pull하기 위해서 NAT Gateway가 필요하다!