기술자료 / 강좌

An Even Easier Introduction to CUDA C / C++
lec_reporterL
CUDA C / C++은 대규모 병렬 어플리케이션을 작성하는 가장 강력한 방법중 하나입니다. 강력한 C++ 프로그래밍 언어를 사용하여 GPU에서 실행되는 수천 개의 병렬 스레드로 가속화되는 고성능 알고리즘의 어플리케이션을 개발할 수 있습니다. 딥 러닝으로 알려진 인공 지능의 지속적인 혁신을 뒷받침하는 라이브러리와 프레임워크를 포함하여 많은 개발자가 이러한 방식으로 계산 및 대역폭을 많이 사용하는 응용 프로그램을 가속화했습니다. ​ CUDA가 지원되는 GPU를 가지고 있는 컴퓨터 또는 클라우드의 GPU 인스턴스가 필요합니다. CUDA Toolkit이 설치되어 있는 상태여야 합니다. (GPU 클라우드는 Jupyter notebook 인터페이스를 쓰겠네요...) ​ 자 이제 시작해 봅시다!! ​ Starting Simple ​ 아래의 샘플 코드는 각각 백만개 원소를 가진 두개의 배열을 더하는 간단한 코드입니다. #include <iostream> #include <math.h> // function to add the elements of two arrays void add(int n, float *x, float *y) { for (int i = 0; i < n; i++) y[i] = x[i] + y[i]; } int main(void) { int N = 1<<20; // 1M elements float *x = new float[N]; float *y = new float[N]; // initialize x and y arrays on the host for (int i = 0; i < N; i++) { x[i] = 1.0f; y[i] = 2.0f; } // Run kernel on 1M elements on the CPU add(N, x, y); // Check for errors (all values should be 3.0f) float maxError = 0.0f; for (int i = 0; i < N; i++) maxError = fmax(maxError, fabs(y[i]-3.0f)); std::cout << "Max error: " << maxError << std::endl; // Free memory delete [] x; delete [] y; return 0; } 일단, 이 C++ 코드를 컴파일 하고, 실행해 봅시다. 코드를 add.cpp 로 저장하고 C++ 컴파일러로 컴파일 합니다. $g++ add.cpp -o add Linux 에서는 g++ 또는 cc Windows 에서는 msvc.exe Mac 에서는 clang++ 를 사용합니다. ​ 그리고, 아래와 같이 실행 합니다. $./add Max error:0.000000 예상했던 것과는 같게... 합하는 동안의 오류가 0.00000 으로 출력되고 종료합니다. 그럼, 이제는 백만번의 더하기를 병렬로 해봅시다. (매우 쉬움) ​ 먼저 더하기 하는 함수를 GPU가 실행할 수 있는 CUDA의 커널이라는 기능으로 바꿔야 합니다. 이렇게 하려면 함수에 지정자 __global__을 추가하기만 하면 됩니다. 이렇게 함으로써 CUDA C 컴파일러에게 이것이 GPU에서 실행되고 CPU 코드에서 호출될 수 있는 함수임을 알려줍니다. // CUDA Kernel function to add the elements of two arrays on the GPU __global__ void add(int n, float *x, float *y) { for (int i = 0; i < n; i++) y[i] = x[i] + y[i]; } 이렇게 global 지정자가 붙어 있는 함수를 커널이라고 한다. GPU에서 실행되는 코드는 디바이스 코드라 하고 CPU에서 실행되는 코드는 호스트 코드라고 하여 구분합니다. ​ Memory Allocation in CUDA ​ GPU에서 계산하려면 GPU에서 액세스할 수 있는 메모리를 할당해야 합니다. CUDA의 통합 메모리는 시스템의 모든 GPU와 CPU에서 액세스할 수 있는 단일 메모리 공간을 제공하여 이를 쉽게 만듭니다. 통합 메모리에 데이터를 할당하려면 호스트(CPU) 코드 또는 디바이스(GPU) 코드에서 액세스할 수 있는 포인터를 반환하는 cudaMallocManaged()를 호출합니다. 데이터를 해제하려면 cudaFree()를 호출하면 됩니다. ​ 위의 코드에서는 new -> cudaMallocManaged() delete [] -> cudaFree() 로 교체하면 됩니다. // Allocate Unified Memory -- accessible from CPU or GPU float *x, *y; cudaMallocManaged(&x, N*sizeof(float)); cudaMallocManaged(&y, N*sizeof(float)); ... // Free memory cudaFree(x); cudaFree(y); 마지막으로 일반 함수에서 커널로 바꾼 add() 커널을 시작하도록 바꿔보자! CUDA 커널의 실행에는 삼중꺽쇠 사이에 <<< >>> 실행 설정 파라메터를 사용하여 지정됩니다. 이 삼중꺽쇠 구문을 원래 함수의 인자들 목록 앞에 추가해 주기만 하면 됩니다. add<<<1,1>>>(N, x, y); 참 쉽죠잉~! 삼중 꺽쇠사이의 파라메터에 대해 설명하기 전에 이 호출은 한개의 GPU 스레드로 실행된다는 것만 알고 넘어 갑시다! ​ ※ One More Thing 당연한 이야기지만 결과에 액세스하기 전에 커널의 실행이 완료될 때까지 CPU는 기다려야 합니다. (CUDA 커널 실행은 호출하는 CPU 스레드가 멈춰서 기다리지 않기 때문에...) 이렇게 기다리려면 CPU에서 최종 오류 검사를 수행하기 전에 cudaDeviceSynchronize()를 호출하면 됩니다. ​ 최종 수정된 코드는 아래와 같습니다. #include <iostream> #include <math.h> // Kernel function to add the elements of two arrays __global__ void add(int n, float *x, float *y) { for (int i = 0; i < n; i++) y[i] = x[i] + y[i]; } int main(void) { int N = 1<<20; float *x, *y; // Allocate Unified Memory – accessible from CPU or GPU cudaMallocManaged(&x, N*sizeof(float)); cudaMallocManaged(&y, N*sizeof(float)); // initialize x and y arrays on the host for (int i = 0; i < N; i++) { x[i] = 1.0f; y[i] = 2.0f; } // Run kernel on 1M elements on the GPU add<<<1, 1>>>(N, x, y); // Wait for GPU to finish before accessing on host cudaDeviceSynchronize(); // Check for errors (all values should be 3.0f) float maxError = 0.0f; for (int i = 0; i < N; i++) maxError = fmax(maxError, fabs(y[i]-3.0f)); std::cout << "Max error: " << maxError << std::endl; // Free memory cudaFree(x); cudaFree(y); return 0; } 이 코드는 CUDA C 파일이라는 표시로 add.cu로 저장합니다. 컴파일은 CUDA C++ 컴파일러인 nvcc 로 아래와 같이 컴파일 합니다. $ nvcc add.cu -o add_cuda $./add_cuda Max error:0.000000 이것은 단순화된 단일 스레드에 대한 첫번째 예제일 뿐이며, 단일 스레드에서만 정확 합니다. 이코드는 실행되는 모든 스레드에서 전체 배열에 대한 더하기를 진행하기 때문이죠. 또한, 이 코드를 여러 스레드로 실행했을경우, 동일한 위치를 읽고 쓸수 있기 때문에 race condition(스레드간 경쟁)상태가 될수 있습니다. Note: Windows의 경우 빌드시 프로젝트 속성의 구성을 플랫폼을 x64로 지정해야 합니다. ​ ​ Profile it! ​ 커널실행에 걸리는 시간을 측정하는 간단한 방법은 CUDA Toolkit에 포함된 Commandline GPU Profiler인 nvprof로 커널을 실행하는 것입니다. 아래와 같이 nvprof ./add_cuda 로 실행합니다. $ nvprof ./add_cuda ==3355== NVPROF is profiling process 3355, command:./add_cuda Max error:0 ==3355==Profiling application:./add_cuda ==3355==Profiling result: Time(%) Time Calls Avg Min Max Name 100.00% 463.25ms 1 463.25ms 463.25ms 463.25ms add(int,float*,float*) ... 위의 결과는 단일 호출로 nvprof의 출력예 입니다. NVIDIA Tesla K80 GPU 에서는 약 0.5초가 소요되고 3년 된 Macbook Pro의 NVIDIA GeForce GT 740M에서는 거의 같은 시간이 걸립니다. ​ 이제, 병렬처리로 더욱 빠르게 만들어 보겠습니다. ​ Picking up the Threads ​ 하나의 스레드로 동작하는 커널을 병렬로 만드는 방법입니다. 핵심은 CUDA의 <<<1, 1>>> 구문에 있습니다. 이것은 실행설정 파라메터로 GPU에서 실행하는 데 사용할 병렬 스레드 수를 CUDA 런타임에 알려줍니다. 여기에는 두 개의 매개변수가 있지만 두 번째 매개변수인 스레드 블록의 스레드 수를 변경하여 시작하겠습니다. CUDA GPU는 크기가 32의 배수인 스레드 블록을 사용하여 커널을 실행하므로 256개의 스레드를 선택하는 것이 적절한 크기로 보입니다. add<<<1,256>>>(N, x, y); 이 커널 실행코드를 바꾸는 것만으로 실행을 하게 되면, 이전에 설명 했듯이 병렬 스레드 간에 계산을 분산하지 않아서 모든 스레드가 전체 배열의 원소에 대해 한번씩 계산을 수행합니다. CUDA C++는 커널이 실행 중인 스레드의 인덱스를 얻을 수 있도록 하는 키워드를 제공합니다. 특히, threadIdx.x는 블록 내 현재 스레드의 인덱스를 포함하고 blockDim.x 블록의 스레드 수를 포함합니다. 이 두개의 미리 정의된 매개변수에 의해 배열내의 요소들이 중복으로 연산 수행에 이용되지 않도록 커널의 루프부분을 수정합시다. __global__ void add(int n, float *x, float *y) { int index = threadIdx.x; int stride = blockDim.x; for (int i = index; i < n; i += stride) y[i] = x[i] + y[i]; } add 함수의 기능은 바뀌지 않았습니다. (전체 배열내의 요소를 더하는...) indedx = 0 으로 stride = 1 로 설정하면 첫번째 소스와 동일한 동작을 합니다. ​ 이 코드를 add_block.cu 로 저장하여 nvcc로 컴파일 하고 nvprof에서 다시 실행합니다. 결과는 아래와 같습니다. Time(%) Time Calls Avg Min Max Name 100.00% 2.7107ms 1 2.7107ms 2.7107ms 2.7107ms add(int,float*,float*) 이 결과는 엄청난 속도의 향상(463ms -> 2.7ms) 이지만, 1스레드에서 256스레드로 바꾸었기 때문에 그리 놀라운 일은 아닙니다. NVIDIA Tesla K80 GPU 는 Macbook pro GPU(3.2ms) 보다 빠릅니다. 계속해서 더 빠른 퍼포먼스를 얻기 위한 방법을 알아 봅시다. ​ Out of the Blocks ​ CUDA GPU에는 Streaming Multiprocessors(SM)로 그룹화된 많은 병렬 프로세서가 있습니다. 각 SM은 여러 개의 동시 스레드 블록을 실행할 수 있습니다. 예를 들어 Pascal GPU 아키텍처 기반 Tesla P100 GPU에는 각각 최대 2048개의 활성 스레드를 지원할 수 있는 56개의 SM이 있습니다. 이 모든 스레드를 최대한 활용하려면 여러 스레드 블록으로 커널을 시작해야 합니다. ​ 이제 실행 설정 파라메터의 첫번째 매개변수가 스레드의 블록의 수를 지정한다는 것을 짐작 하겠죠? 또한, 병렬 스레드 블록이 그리드로 알려진 것을 구성합니다. 처리할 N개의 요소와 블록당 256개의 스레드가 있으므로 최소한 N개의 스레드를 얻으려면 블록 수를 계산하면 됩니다. 편하게 하기 위해 단순히 N을 블록 크기로 나눕니다(N이 blockSize의 배수가 아닌 경우 반올림에 주의) int blockSize =256; int numBlocks =(N + blockSize -1)/ blockSize; add<<<numBlocks, blockSize>>>(N, x, y); [image: 1776544769869-cuda_indexing.png] 또한 스레드 블록의 전체 그리드를 고려하도록 커널 코드를 업데이트해야 합니다. CUDA는 그리드의 블록 수를 포함하는 gridDim.x와 그리드에 있는 현재 스레드 블록의 인덱스를 포함하는 blockIdx.x를 제공합니다. 그림1 은 blockDim.x, gridDim.x 및 threadIdx.x를 사용하여 CUDA에서 배열(1차원)로 인덱싱하는 접근 방식을 보여줍니다. 아이디어는 각 스레드가 블록의 시작 부분에 대한 오프셋을 계산하고 (블록 인덱스 곱하기 블록 크기: blockIdx.x * blockDim.x) 블록 내에 스레드의 인덱스(threadIdx.x)를 더하여 인덱스를 얻는다는 것입니다. 코드 ockIdblx.x * blockDim.x + threadIdx.x는 관용적으로 쓰이는 CUDA 코드 입니다. __global__ void add(int n, float *x, float *y) { int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = blockDim.x * gridDim.x; for (int i = index; i < n; i += stride) y[i] = x[i] + y[i]; } 이렇게 업데이트된 커널은 stride를 그리드의 총 스레드 수로 설정합니다. (blockDim.x * gridDim.x) CUDA에서 이러한 유형의 루프는 grid-stride loop 라고 합니다. ​ 파일을 add_grid.cu로 저장하고 nvcc로 컴파일하고 nvprof로 실행합니다. Time(%) Time Calls Avg Min Max Name 100.00% 94.015us 1 94.015us 94.015us 94.015us add(int,float*,float*) 8배 빨라진 결과를 보여줍니다~! ​ Summing Up ​ 다음은 Tesla K80 및 GeForce GT 750M에서 세 가지 버전의 add() 커널 성능에 대한 요약입니다. [image: 1776545597737-cu_lec2.png] 위의 표에서 보는것과 같이 GPU에서 매우 높은 대역폭을 얻을 수 있습니다. 이 예제에서는 아주 기본적이면서 제한적인 대역폭만을 사용했지만, GPU는 고밀도행렬, 선형대수, 딥러닝, 이미지 프로세싱, 신호처리, 물리 시뮬레이션 등과 같이 컴퓨팅 파워를 많이 사용하는 계산에서도 탁월합니다. ​ NVIDIA 개발자 싸이트나 싸이트내의 NVIDIA 개발자 블로그에는 CUDA C++ 및 기타 GPU 컴퓨팅 주제에 대한 다양한 콘텐츠가 있으니 더 깊이 있는 학습을 위해 이 싸이트들을 이용하기를 권장 드립니다.
더 보기

자유로운 이야기 나눔

기술이 발전해도 의외로 안 바뀌는 사용 습관에 대한 생각
V
기술이 아무리 발전해도, 우리가 포기 못 하는 아날로그적 습관들** 요즘 들어 문득 이런 생각이 들어요. 우리가 사는 세상은 정말 놀라울 정도로 첨단 기술의 집약체잖아요. 스마트폰 하나만 봐도, 인공지능이 우리의 일상 깊숙한 곳까지 들어와서 '더 효율적으로', '더 편리하게' 이 모든 걸 재정의하고 있는 것 같아요. 모든 게 데이터화되고, 즉각적인 반응이 미덕이 된 시대잖아요. 그런데 신기하게도, 이 거대한 기술적 물결 속에서도 우리의 가장 사적인 감각적 리듬이나 아주 오래된 생활 습관들은 마치 시간이 멈춘 것처럼 끈질기게 남아있는 것 같다는 겁니다. 예를 들어, 저는 요즘도 중요한 음악을 듣거나 감상할 때는 굳이 스트리밍 서비스의 알고리즘 추천을 따르기보다, 예전에 쓰던 플레이리스트 폴더를 뒤적거리면서 '이때 들었던 노래'라는 감성적인 경로를 거쳐 가려고 애쓰는 저 자신을 발견하곤 해요. 혹은 사진을 찍을 때도, 화질이나 해상도에 집착하기보다, 빛이 어떻게 들어오는지, 그 순간의 '분위기'라는 아날로그적 느낌을 담으려고 셔터를 누르는 게 더 중요하게 느껴질 때가 많아요. 이 모든 게 결국 '최적화'의 시대에 살면서도, 어딘가 모르게 '불완전함'이나 '느림'에서 오는 안정감을 놓지 못하고 있다는 증거가 아닐까 싶어요. 기술은 우리에게 무한한 가능성을 열어줬지만, 그 가능성의 홍수 속에서 오히려 우리가 가장 편안함을 느끼는 '의도적인 비효율'을 그리워하고 있는 건 아닐까요? 이런 현상을 깊이 파고들다 보니, 이게 단순히 '추억팔이'라기보다는, 인간의 뇌가 정보를 처리하는 근본적인 방식과 관련이 있는 것 같다는 생각이 들어요. 디지털 환경은 모든 것을 '검색'하고 '분류'하도록 강요하지만, 우리의 감성적 경험은 종종 '우연한 발견'이나 '흐름' 속에서 가장 깊은 만족감을 얻잖아요. 예를 들어, 책을 읽을 때도 그렇죠. 유튜브나 웹 기사는 '요약본'을 원하지만, 결국 우리는 서점 구석에서 우연히 손에 들어온 작가 이름이나, 책의 모서리에 묻어있는 누군가의 메모 같은 '잡다한 파편'들에서 더 큰 매력을 느끼는 것 같아요. 그 파편들에는 그 책이 거쳐온 시간의 흔적, 즉 인간의 손길과 삶의 냄새가 배어있거든요. 기술은 너무 완벽해서, 오히려 그 '인간적 결함'이 주는 따뜻함에 목마른 건지 모르겠어요. 어쩌면 우리가 그토록 갈망하는 궁극적인 편리함이란, 기술이 우리를 대신해주기를 바라는 것이 아니라, 기술의 도움을 받아 '우리가 가장 인간적인 리듬'을 잃지 않고 유지하는 방법을 찾는 것일지도 모른다는 생각이 들어요. 결국 최첨단 기술의 발전은 우리를 더 빠르고 많이 연결했지만, 역설적으로 우리 내면의 가장 느리고 느린 감각들을 더욱 예민하게 깨우고 있는 건 아닐까요? 우리가 아무리 첨단 기술에 둘러싸여도, 감각적 만족감의 근원은 여전히 아날로그적인 리듬에 남아있다. ** 결국 기술의 발전은 우리에게 더 많은 도구를 주었을 뿐, 가장 근원적인 인간의 감성적 리듬을 대신 채워주지는 못한다.
더 보기

유머 / 이슈 게시판

남자들 PTSD 온다는 갤럭시 버즈 케이스.jpg
M
재미있는 장면이네요. [image: humor_7f633d8447_01.webp]
더 보기

Question & Answer

개인/공유 비밀번호 관리자 추천받고 싶어요
S
안녕하세요. 비밀번호 관리자 관련해서 고민이 많으시군요. 저도 예전에 비슷한 고민을 많이 했었어요. 처음에는 '최고의 보안'이라는 말만 들으니까, 기능이 너무 복잡하고 설정할 게 산더미인 툴들만 눈에 들어오더라고요. 막상 써보려고 하니 '이걸 다 설정해야 하나?' 싶어서 금방 질리기도 하고요. 말씀해주신 '가볍게 쓰면서 보안을 챙기고, 필요할 때만 구조적으로 공유할 수 있는' 이 니즈가 핵심인 것 같아요. 이게 사실 비밀번호 관리자 시장에서 가장 어려운 지점 중 하나거든요. 너무 가벼우면 보안에 구멍이 생기고, 너무 무거우면 사용성이 떨어진다는 딜레마랄까요. 제가 직접 몇 가지 툴들을 써보면서 느낀 점이랑, 질문자님의 조건에 맞춰서 몇 가지 방향으로 나누어 장단점과 실질적인 팁을 드리려고 합니다. 참고만 해주시면 좋겠습니다. *** ### 1. 개인 사용에 초점을 맞출 경우 (가벼움 + 높은 보안) 개인용으로 쓰실 거라면, 일단 **'사용 편의성(UX)'**과 **'기본 암호화 강도'**에 초점을 맞추는 게 좋습니다. 제가 추천하는 방향은 1Password나 Bitwarden 같은 툴들입니다. (물론 추천이 아닐 수도 있지만, 많은 분들이 이 두 가지를 두고 많이 비교하시더라고요.) * Bitwarden: 만약 비용을 최대한 아끼고 싶고, 오픈소스 기반의 투명성을 중요하게 생각하신다면 강력하게 추천합니다. 무료 버전만으로도 개인 용도로는 충분한 보안 수준을 제공해요. 개인 사용자 입장에서는 설정할 게 많다는 느낌보다는, 필요한 항목만 넣고 나가는 '미니멀리즘' 접근이 가능합니다. 1Password: 유료 쪽으로 기울어지긴 하지만, 사용자 인터페이스(UI/UX)가 정말 직관적이에요. 복잡한 보안 설정보다는 '어떤 정보가 어디에 있는지'를 시각적으로 잘 정리해주는 느낌이 강합니다. 개인적으로는 이 '직관성' 때문에 오히려 사용하기 편하다고 느꼈어요. 실사용 팁 (개인용): 개인용으로 쓰실 때 가장 흔하게 하는 실수는, **마스터 비밀번호(가장 중요한 그 비밀번호)**를 너무 쉽게 설정하거나, 그 비밀번호를 어디엔가 적어두는 경우입니다. 이건 정말 최악의 실수예요. 마스터 비밀번호는 기억하기 어렵지만, 절대로 외부에 기록하지 않고, 다른 비밀번호와 전혀 연관성이 없는 '암호 문장' 형태로 기억하시는 게 좋습니다. 예를 들어, 좋아하는 노래 가사 일부를 조합해서 외우는 식이죠. *** ### 2. 공유/팀 협업에 초점을 맞출 경우 (구조적 공유 + 보안 리스크 최소화) 여기가 가장 까다로운 부분이죠. 공유할 때는 '누구에게', '어떤 정보를', '언제까지' 보여줄지 이 '권한 관리(Permission Management)'가 생명입니다. 이 경우에는 1Password나 LastPass Business 같은 '팀 플랜'이 구조적으로 더 유리할 때가 많습니다. 구조화된 공유의 핵심: 팀 플랜들은 보통 'Vault(금고)'라는 단위 안에 폴더(또는 컬렉션)를 만들고, 그 폴더 단위로 멤버별 접근 권한을 설정할 수 있게 해줍니다. 예를 들어, '회사 공용 계정 비밀번호' 폴더는 A팀원만 보기, '마케팅 캠페인용 비밀번호' 폴더는 B팀원만 보기 식으로 쪼갤 수 있다는 거죠. 보안 리스크 최소화 팁 (공유 시): 1. 최소 권한 원칙(Principle of Least Privilege) 적용: 이건 보안 분야에서 가장 중요한 원칙인데, 말 그대로 '필요한 만큼만' 권한을 주는 거예요. 예를 들어, 팀원 A가 특정 비밀번호를 '읽기 전용'으로만 접근하면 되고, '수정' 권한은 필요 없다면 아예 주지 않는 거죠. 만료일 지정: 공유하는 비밀번호 자체가 만료되도록 설정하고, 해당 시점에 '비밀번호를 재설정할 사람'을 명확하게 지정해두는 것이 좋습니다. 3. 비밀번호 덩어리화 금지: '모든 비밀번호를 한 군데에 때려 넣고, 모든 사람에게 접근 권한을 주는 것'은 절대 금물입니다. 목적별로 금고를 분리하세요. *** ### 3. 질문자님의 요구사항에 가장 근접한 '절충안' 제안 질문자님이 원하시는 '가볍지만 구조적인 공유'에 가장 근접한 건, **'개인용으로 시작하되, 공유 기능이 유연한 툴'**을 사용하는 겁니다. 저는 현재까지의 경험을 토대로 1Password를 한 번 더 고려해보시라고 말씀드리고 싶어요. 왜 1Password인가? (제가 느끼기엔 이렇습니다) 1. 사용성: UI가 직관적이라 '너무 복잡하다'는 느낌을 덜 받을 가능성이 높습니다. 2. 유연성: 개인적으로는 1인 계정으로 사용하다가, 팀이 생겼을 때 유료 플랜으로 전환하면서 '권한 관리'라는 구조를 비교적 부드럽게 추가할 수 있는 느낌을 받았습니다. 3. 공유의 구조화: 공유 시에도 '어떤 정보가 어느 팀/프로젝트에 속하는지'를 폴더 구조로 명확하게 분리할 수 있도록 유도합니다. ️ 하지만 만약 유료 결제나 복잡한 구독 모델이 부담되신다면, 'Notion' + 'Password Manager' 조합을 고려해볼 수도 있습니다. 이건 약간 비주류 팁인데요, 비밀번호 자체는 Bitwarden 같은 전용 툴에 넣어두고, 그 비밀번호가 '어떤 프로젝트'에 쓰이는지, '누가' 이 정보를 관리하는지 같은 '메타데이터'나 '가이드라인'을 Notion 같은 문서 툴에 구조화하여 관리하는 방식입니다. 장점: 문서 관리의 유연성이 극대화됩니다. (문서 자체에 메모, 담당자, 회의록 등을 붙일 수 있음) * 단점 (주의할 점): 이건 **'백업 시스템'**에 가깝고, 비밀번호 그 자체를 관리하는 보안 전문 툴과는 결이 다릅니다. 비밀번호 자체의 암호화나 자동 입력 기능은 전용 툴이 압도적으로 좋습니다. 즉, '문서 관리'와 '비밀번호 관리'를 이원화해서 생각하셔야 합니다. *** ### 최종 정리 및 점검 체크리스트 결론적으로, 사용 목적에 따라 추천하는 방향이 달라지니, 질문자님 스스로 아래 질문에 답해보시고 툴을 선택해보세요. 가장 중요한 것은 무엇인가요? * A. 사용하기 너무 편한 것 (직관성) $\rightarrow$ 1Password 쪽 검토 * B. 비용 절감 및 오픈소스 투명성 $\rightarrow$ Bitwarden 쪽 검토 * C. 복잡해도 상관없으니, 가장 강력한 그룹 권한 관리가 필요하다 $\rightarrow$ 유료 팀 플랜 검토 2. 공유하는 정보의 민감도는 어느 정도인가요? * 아주 높음 (회사 기밀 등) $\rightarrow$ 반드시 **2단계 인증(2FA)**이 강제되고, 감사 로그(Audit Log) 기능이 있는 유료 솔루션이 좋습니다. 중간 (개인 프로젝트 등) $\rightarrow$ 무료/저가 솔루션으로도 충분합니다. 흔한 실수 요약 (다시 한번 강조드립니다): 비밀번호 관리자 툴을 '만병통치약'처럼 생각하고 모든 것을 넣으려고 하면 복잡해집니다. '개인용 금고' (나의 비밀번호들) '공유용 금고' (팀/프로젝트 비밀번호들) 이렇게 목적에 따라 금고를 분리하는 습관을 들이는 것만으로도 80%는 해결될 겁니다. 이 답변이 조금이나마 고민 해결에 도움이 되었으면 좋겠네요. 사용하시다가 또 막히는 부분 있으면 언제든지 다시 질문주세요!
더 보기