존 헤네시의 핵심 아키텍처 관점: 왜 성능 향상이 더 이상 ‘공짜’로 오지 않는지, 병렬성이 어떻게 도움되는지, 그리고 현대 시스템을 규정하는 트레이드오프는 무엇인지 살펴봅니다.

존 헤네시는 컴퓨터가 왜 빨라지는지—그리고 그 진전이 때로는 왜 멈추는지를 가장 명확하게 설명한 설계자 중 하나다. 영향력 있는 프로세서를 설계하고 RISC 개념을 대중화한 것 외에도, 그는 시스템 설계자들에게 성능 결정을 위한 실용적 어휘를 제공했다: 무엇을 최적화할지, 무엇을 최적화하지 않을지, 그리고 둘을 구분하는 방법이다.
사람들이 “성능 스케일링”이라고 할 때 흔히 의미하는 건 “내 프로그램이 더 빨라진다”는 것이다. 실제 시스템에서 스케일링은 속도, 비용, 전력/에너지 사이의 세자간 협상이다. 어떤 변경이 특정 워크로드를 20% 빠르게 만들더라도 칩 비용을 올리거나 서버의 냉각을 어렵게 하거나 배터리 소모를 빠르게 할 수 있다. 헤네시의 관점이 중요한 이유는 이러한 제약들을 불쾌한 놀라움이 아니라 정상적인 공학 입력으로 다루기 때문이다.
첫째는 병렬성: 동시에 더 많은 일을 수행하는 것. 이는 코어 내부(명령 수준 기법), 코어 간(스레드), 전체 머신 단위에서 나타난다.
둘째는 특수화: 작업에 맞는 도구를 쓰는 것. GPU, 비디오 인코더, ML 가속기는 범용 CPU가 모든 것을 효율적으로 처리할 수 없기 때문에 존재한다.
셋째는 트레이드오프: 모든 “이득”에는 대가가 있다. 핵심은 한계가 어디에 있는지—연산, 메모리, 통신, 에너지—를 이해하는 것이다.
이 글은 전기적 전기(전기? 전기 아닙니다) 전기적 약력 전개가 아니다. 대신 벤치마크를 읽거나 하드웨어를 선택하거나 수요에 따라 성장해야 하는 소프트웨어를 설계할 때 적용할 수 있는 실용적 개념들을 제시한다.
오랜 기간 동안 컴퓨팅 역사에서 성능 향상은 거의 자동처럼 느껴졌다. 트랜지스터가 작아지면서 칩 제조사는 더 많은 트랜지스터를 한 프로세서에 집적하고 종종 더 높은 클럭으로 동작시킬 수 있었다. 소프트웨어 팀은 같은 프로그램을 새 머신에서 실행하면 더 빠르게 종료되는 것을 보고 별다른 재설계 없이 성능 향상을 경험했다.
새 CPU 세대가 더 높은 GHz, 트랜지스터당 낮은 비용, 일상 코드에서 눈에 띄는 속도 향상을 자주 의미하던 시기다. 이러한 이득의 상당 부분은 개발자가 다르게 생각할 필요 없이 컴파일러와 하드웨어 업그레이드가 큰 역할을 했다.
결국 더 높은 클럭은 전력과 발열이 너무 빠르게 증가하기 때문에 더 이상 단순한 승리가 아니게 되었다. 트랜지스터를 작게 만드는 것이 예전처럼 자동으로 전력을 줄여주지 않았고, 주파수를 올리면 칩이 더 뜨거워졌다. 어느 순간 제한 요소는 ‘더 빠르게 만들 수 있느냐’가 아니라 ‘믿을 수 있게 전력 공급과 냉각을 할 수 있느냐’가 되었다.
자동차 엔진을 생각해보라. 더 빠르게 달리려면 회전수를 올리면 되지만 한계가 있다: 연료 소비가 급증하고 부품이 과열되며 시스템이 안전하지 않게 된다. CPU도 비슷한 한계에 부딪힌다. ‘RPM’(클럭)을 올리는 것은 비례보다 훨씬 더 많은 에너지를 소모하고 더 많은 열을 만든다.
클럭 스케일링이 둔화되자 성능은 설계를 통해 ‘획득’해야 하는 것이 되었다: 더 많은 병렬 작업, 캐시와 메모리의 더 나은 활용, 특수 하드웨어, 신중한 소프트웨어 선택. 헤네시의 메시지는 이 변화에 부합한다. 큰 이득은 이제 하드웨어와 소프트웨어 전체가 함께 작동하도록 설계할 때 나오며, 다음 칩이 자동으로 문제를 해결해주기를 기대해서는 안 된다.
명령 수준 병렬성(Instruction-Level Parallelism, ILP)은 코어 내부에서 작은 단계들을 동시에 수행하는 것이다. 프로그램이 ‘싱글 스레드’여도, 프로세서는 서로 의존하지 않는 한 한 명령이 기다리는 동안 다른 명령을 시작할 수 있다.
ILP를 단순히 떠올리는 방법은 파이프라이닝이다. 조립 라인을 생각해보라: 한 단계는 명령을 가져오고, 다음은 디코드하고, 다음은 실행하고, 다음은 결과를 쓴다. 파이프라인이 채워지면 CPU는 각 명령이 여러 단계를 거치더라도 대략 사이클당 한 개의 명령을 ‘완료’할 수 있다.
파이프라이닝은 프로그래머가 모든 것을 다시 작성할 필요 없이 처리량을 개선했기 때문에 수년간 성능 향상에 기여했다.
실제 프로그램은 직선으로만 실행되지 않는다. 조건문(“if this, then that”)이 있고 CPU는 다음에 무엇을 가져올지 결정해야 한다. 만약 그 결과를 기다리면 파이프라인이 멈출 수 있다.
**분기 예측(branch prediction)**은 CPU가 다음 경로를 추측해서 작업 흐름을 유지하는 방식이다. 예측이 맞으면 성능은 유지된다. 틀리면 CPU는 잘못된 경로의 작업을 버리고 벌점을 치른다—낭비된 사이클과 낭비된 에너지다.
ILP를 더 밀어붙이려면 독립적인 명령을 찾고, 안전하게 재정렬하고, 잘못된 분기를 복구할 하드웨어가 더 필요하다. 이는 복잡성과 검증 노력을 늘리고 전력 사용을 증가시키며 세대가 지날수록 더 작은 이득을 주는 경향이 있다.
이는 헤네시의 반복되는 교훈 중 하나다: ILP는 가치가 있지만 실용적 한계에 부딪히므로 지속적인 성능 확장을 위해서는 단일 코어 실행의 ‘더 똑똑한’ 방법들만으로는 부족하다.
암달의 법칙(Amdahl’s Law)은 작업의 일부를 빠르게 해도 남은 느린 부분이 전체를 얼마나 가속할 수 있는지의 상한을 상기시켜준다. 무거운 수학은 필요 없다—단지 병렬화할 수 없는 부분이 무엇인지 알아차리면 된다.
한 명의 고객과 체크아웃 과정을 가진 식료품점을 상상해보자:
결제가 전체 시간의 10%를 항상 차지한다면, 스캔을 더 많은 계산대로 ‘즉시’ 만들더라도 전체적으로는 약 10× 이상의 속도 향상을 얻을 수 없다. 직렬 부분이 상한이 된다.
요리도 같은 패턴을 보인다: 물이 끓는 동안 채소를 썰 수는 있지만(병렬), 오븐에서 30분 동안 구워야 하는 케이크는 병렬화할 수 없다.
핵심 통찰은 직렬 작업의 마지막 몇 퍼센트가 모든 것을 제한한다는 것이다. “99% 병렬”인 프로그램은 놀라운 것처럼 들리지만 많은 코어에 걸쳐 확장하려 할 때 그 1% 직렬 부분이 병목이 된다.
암달의 법칙은 ‘코어만 더 추가하라’는 접근이 자주 실망스러운 이유다. 코어를 더 추가하려면 충분한 병렬 작업이 존재해야 하고, 동기화, I/O, 싱글 스레드 단계, 메모리 정지 등 직렬 병목이 작게 유지되어야 한다.
또한 가속기가 까다로운 이유도 설명한다: GPU가 한 커널을 가속화해도 파이프라인의 나머지 부분이 직렬 상태라면 전체 이득은 제한적일 수 있다.
병렬화에 투자하기 전에 물어보라: 실제로 어느 비율이 병렬이고, 어느 부분이 직렬로 남는가? 그리고 시간이 실제로 가는 곳—종종 ‘지루한’ 직렬 경로—에 노력을 쏟아라. 그것이 한계를 설정한다.
수년간 성능 향상은 주로 단일 CPU 코어를 더 빠르게 만드는 것을 의미했다. 하지만 이 접근법은 실용적 한계에 부딪혔다: 더 높은 클럭은 발열과 전력을 증가시켰고, 더 깊은 파이프라인은 실세계에서 비례적인 속도 향상으로 이어지지 않았다. 주류의 해법은 한 칩에 여러 코어를 넣고 동시에 더 많은 일을 하게 하는 것이었다.
멀티코어는 두 가지 방식에서 유용하다:
이 구분은 계획 수립에서 중요하다: 서버는 더 많은 요청을 동시에 처리하는 것만으로도 즉시 이득을 볼 수 있지만, 데스크톱 애플리케이션은 자체 작업이 병렬화되어야 체감 속도가 개선된다.
스레드 수준 병렬성은 자동으로 주어지지 않는다. 소프트웨어는 스레드, 작업 큐, 또는 작업을 독립 단위로 분할하는 프레임워크를 통해 병렬 작업을 노출해야 한다. 목표는 서로 계속 기다리지 않고 코어를 바쁘게 유지하는 것이다.
일반적인 실제적 조치로는 루프 병렬화, 독립 단계의 분리(예: 디코드 → 처리 → 인코드), 여러 요청/이벤트 동시 처리 등이 있다.
멀티코어 확장은 종종 다음과 같은 오버헤드에서 멈춘다:
헤네시의 넓은 메시지는 여기에 적용된다: 병렬성은 강력하지만 실제 속도 향상은 신중한 시스템 설계와 솔직한 측정에 달려 있다—그저 코어를 더 추가한다고 해결되진 않는다.
CPU는 손에 데이터가 있을 때만 작업할 수 있다. 데이터가 아직 메모리에서 오는 중이라면 CPU는 기다려야 한다. 이 대기 시간은 **메모리 지연(latency)**이고, 이로 인해 ‘빠른’ 프로세서가 비싼 유휴 머신이 될 수 있다.
메모리를 먼 창고로 비유해보라. 일꾼들(CPU 코어)이 매우 빠르더라도 부품이 교통 체증에 걸리면 조립을 할 수 없다. 현대 프로세서는 초당 수십억 연산을 실행할 수 있지만 메인 메모리 접근은 수백 CPU 사이클이 걸릴 수 있다. 그 빈틈이 쌓인다.
대기 시간을 줄이기 위해 컴퓨터는 CPU에 더 가까운 작고 빠른 메모리 영역인 캐시를 사용한다—마치 자주 쓰는 부품을 비치한 근처 선반 같다. 필요한 데이터가 이미 선반에 있다(“캐시 히트”)면 작업은 매끄럽게 계속된다. 없다면(“미스”), CPU는 더 먼 곳에서 가져와야 하고 전체 지연 비용을 치른다.
지연은 “첫 항목이 도착할 때까지 얼마나 걸리느냐”다. 대역폭은 “초당 얼마나 많은 항목이 도착할 수 있느냐”다. 넓은 고속도로(높은 대역폭)가 있어도 여전히 긴 거리(높은 지연)를 가질 수 있다. 어떤 워크로드는 많은 데이터를 스트리밍하므로 대역폭에 제약을 받고, 어떤 워크로드는 작은 흩어진 조각을 자주 필요로 하므로 지연에 취약하다. 어느 쪽이든 시스템이 느리게 느껴질 수 있다.
헤네시의 한계에 관한 큰 관점은 여기서 메모리 월로 드러난다: CPU 속도는 수년간 메모리 접근 시간보다 더 빨리 향상되어 프로세서가 기다리는 시간이 늘어났다. 그래서 성능 향상은 종종 데이터 지역성 향상(캐시가 더 도움이 되도록), 알고리즘 재고, 또는 시스템 균형 변경에서 나오며 단순히 CPU 코어를 빠르게 만드는 것만으로는 부족하다.
오랫동안 “더 빠르게”는 주로 “클럭을 더 높인다”는 뜻이었다. 하지만 전력을 하드 예산으로 취급하면 그 사고방식은 무너진다. 추가 와트는 냉각해야 할 열이 되고, 소모해야 할 배터리가 되거나 비용으로 이어진다. 성능은 여전히 목표지만, 무엇이 실제로 출하되고 확장되는지를 결정하는 건 성능당 전력(Performance per watt)이다.
전력은 단순한 기술적 세부사항이 아니다; 제품 제약이다. 벤치마크에서 잘 나오는 노트북이 2분 후 스로틀링(throttling)을 한다면 체감 성능은 느리다. 페이지를 즉시 렌더링하지만 배터리를 20% 잃는 폰은 좋은 제품이 아니다. 서버에서도 여유 연산 용량이 있어도 여유 전력이나 냉각 여유가 없을 수 있다.
주파수를 올리는 것은 불균형적으로 비용이 크다. 전압과 스위칭 활동을 밀어붙이면 전력은 급격히 증가한다. 단순화하면 동적 전력은 대체로 다음과 같은 이유로 증가한다:
그래서 마지막 10–20%의 클럭 속도를 얻기 위해 훨씬 큰 와트 상승이 필요하며—이는 지속 가능한 이득 대신 열 한계와 스로틀링을 불러올 수 있다.
이것이 현대 설계가 효율성을 중시하는 이유다: 병렬성의 넓은 활용, 더 똑똑한 전력 관리, ‘충분히 좋은’ 클럭과 더 나은 마이크로아키텍처의 조합. 데이터센터에서는 전력이 하드웨어 비용과 견줄 만한 항목이 된다. 클라우드에서는 비효율적인 코드는 직접적으로 비용을 증가시킨다—사용 시간, 코어, 그리고(종종 간접적으로) 에너지를 지불하기 때문이다.
헤네시의 반복되는 요점은 단순하다: 성능 스케일링은 하드웨어 문제만도, 소프트웨어 문제만도 아니다. 하드웨어–소프트웨어 공동 설계는 CPU 특성, 컴파일러, 런타임, 알고리즘을 실제 워크로드에 맞춰 정렬하는 것을 의미한다—그래서 시스템은 스펙 시트에 멋있게 보이는 것이 아니라 실제로 당신이 실행하는 것에서 더 빨라진다.
고전적 예는 하드웨어 기능을 열어주는 컴파일러 지원이다. 프로세서에 넓은 벡터 유닛(SIMD), 분기 예측, 또는 연산을 결합하는 명령이 있더라도 소프트웨어가 구조화되어 컴파일러가 이를 안전하게 사용할 수 있어야 한다.
병목이 메모리 정지, 락 경쟁, 또는 I/O에 있다면 더 높은 클럭이나 더 많은 코어는 거의 효과가 없을 수 있다. 시스템은 단지 같은 한계에 더 빨리 도달할 뿐이다. 소프트웨어 변경—더 나은 병렬 구조, 더 적은 캐시 미스, 적은 동기화—없이는 새 하드웨어가 놀고 있을 수 있다.
플랫폼이나 최적화를 고려할 때 물어보라:
RISC(축소 명령어 집합 컴퓨팅)는 슬로건이라기보다 전략적 선택이다: 명령어 집합을 작고 규칙적으로 유지하면 각 명령을 빠르고 예측 가능하게 실행할 수 있다. 존 헤네시는 하드웨어의 역할을 단순화하면(비록 소프트웨어가 더 많은 명령을 사용하더라도) 성능이 오히려 좋아질 수 있다는 생각을 대중화했다.
간소화된 명령어 집합은 일관된 포맷과 직관적인 연산(load, store, add, branch)을 갖는 경향이 있다. 그런 규칙성은 CPU가 다음을 더 쉽게 하게 한다:
핵심은 명령을 다루기 쉬우면 프로세서는 예외와 특수 케이스를 관리하는 데 드는 시간을 줄이고 더 많은 시간을 실제 작업에 쓸 수 있다는 것이다.
복잡한 명령은 프로그램이 필요로 하는 명령 수를 줄일 수 있지만 하드웨어 복잡성—더 많은 회로, 더 많은 코너 케이스, 제어 로직에 소비되는 전력—을 증가시키는 경향이 있다. RISC는 이를 뒤집는다: 더 단순한 빌딩 블록을 사용하고 컴파일러와 마이크로아키텍처가 속도를 뽑아내게 하라.
이것은 에너지 효율로도 이어질 수 있다. 오버헤드와 제어에 낭비되는 사이클이 적은 설계는 줄어든 줄량의 줄 단위(joule)를 낭비하게 된다. 전력과 열이 칩의 지속적 동작 속도를 제약할 때 이 점은 중요하다.
휴대폰, 노트북, 서버의 현대 CPU는 RISC 스타일 원칙을 널리 차용한다: 규칙적인 실행 파이프라인, 단순 연산에 대한 최적화, 컴파일러에 대한 강한 의존 등. ARM 기반 시스템은 RISC 계열이 주류 컴퓨팅에 진입한 눈에 띄는 사례지만, 본질적 교훈은 ‘어떤 브랜드가 이기느냐’가 아니다.
지속적인 원칙은: 더 높은 처리량, 더 나은 효율성, 그리고 핵심 아이디어의 확장이 가능해진다면 단순함을 선택하라.
특수화는 범용 CPU에 모든 것을 시키는 대신 특정 종류의 작업을 극도로 잘 수행하도록 만든 하드웨어를 사용하는 것이다. 일반적 예로는 그래픽과 병렬 수학을 위한 GPU, 행렬 연산을 위한 AI 가속기(NPU/TPU), H.264/HEVC/AV1 같은 비디오 코덱용 고정 기능 블록이 있다.
CPU는 유연성을 위해 설계된다: 많은 명령, 많은 제어 로직, 분기성 코드의 빠른 처리. 가속기는 그 유연성을 효율성으로 바꾼다. 칩 자원을 실제로 필요한 연산(예: multiply–accumulate)에 더 많이 할당하고 제어 오버헤드를 최소화하며 정확도가 허용하는 경우 낮은 정밀도(INT8, FP16 등)를 사용하는 경향이 있다.
그 결과 작업당 더 많은 일을 와트당 할 수 있다: 적은 명령, 적은 데이터 이동, 더 많은 병렬 실행. 렌더링, 추론(inference), 인코딩처럼 반복적인 커널이 지배적인 워크로드에서는 전력 관리 하에서 극적인 속도 향상을 가져올 수 있다.
특수화에는 비용이 따른다. 유연성을 잃을 수 있으며(하드웨어가 한 작업에는 뛰어나지만 다른 작업에는 형편없음), 더 높은 엔지니어링 및 검증 비용을 지불할 수 있고 드라이버·컴파일러·라이브러리 같은 소프트웨어 생태계에 의존하게 되어 지연이나 벤더 락인 위험이 있다.
가속기를 선택하라면:
워크로드가 불규칙하거나 빠르게 변하거나 소프트웨어 비용이 절감액보다 클 때는 CPU를 유지하라.
컴퓨터 아키텍처에서의 모든 성능 “승리”에는 대가가 따른다. 헤네시의 작업은 반복해서 실용적 진실로 돌아간다: 시스템을 최적화한다는 것은 포기할 것을 선택하는 것이다.
몇 가지 긴장은 반복해서 나타난다:
하나의 숫자에 맞춰 최적화하다가 사용자 경험을 해칠 수 있다.
예를 들어 클럭 속도를 높이면 전력과 열이 증가해 스로틀링이 발생하고 지속 성능이 떨어질 수 있다. 코어를 추가하면 병렬 처리량은 늘어나지만 메모리 경쟁이 증가해 각 코어의 효율이 떨어질 수 있다. 큰 캐시는 미스율을 줄여(지연에 유리) 반면 칩 면적과 접근당 소비 전력이 증가(비용과 효율성에 불리)할 수 있다.
헤네시의 성능 관점은 실용적이다: 당신이 신경 쓰는 워크로드를 정의한 뒤 그 현실에 맞춰 최적화하라.
수백만의 유사한 요청을 처리하는 서버는 예측 가능한 처리량과 작업당 에너지를 신경 쓴다. 노트북은 반응성 및 배터리 수명을 신경 쓴다. 데이터 파이프라인은 총 작업 시간이 개선된다면 높은 지연을 수용할 수 있다. 벤치마크와 스펙은 유용하지만 실제 사용 사례와 맞아야 의미가 있다.
다음 같은 열을 가진 작은 표를 추가하는 것을 고려하라: 결정, 도움, 해침, 적합 대상. 행에는 “코어 증가”, “캐시 확대”, “주파수 증가”, “벡터 유닛 확대”, “더 빠른 메모리” 등을 넣어 트레이드오프를 구체화하면 논의를 결과에 묶어둘 수 있다.
성능 주장은 그것을 뒷받침하는 측정만큼만 신뢰할 수 있다. 벤치마크는 완전히 ‘정확’하더라도 실제 워크로드를 닮지 않으면 오해를 낳을 수 있다: 데이터 크기, 캐시 동작, I/O 패턴, 동시성, 읽기 대 쓰기 비율 등이 다르면 결과가 뒤집힐 수 있다. 그래서 헤네시 계열의 설계자들은 벤치마킹을 트로피가 아니라 실험으로 다룬다.
처리량은 단위 시간당 얼마나 많은 작업을 끝내는지(요청/초, 작업/시간)다. 용량 계획에 좋지만 사용자는 평균을 체감하지 않는다.
**꼬리 지연(tail latency)**은 가장 느린 요청에 초점을 맞춘다—종종 p95/p99로 보고된다. 시스템은 평균 지연이 훌륭해도 p99가 끔찍할 수 있는데, 이는 큐잉, GC 정지, 락 경쟁, 혹은 노이즈 이웃 때문에 발생한다.
**활용률(utilization)**은 자원이 얼마나 ‘바쁜지’(CPU, 메모리 대역폭, 디스크, 네트워크)를 뜻한다. 높은 활용률은 좋을 수 있지만 큐가 길어져 꼬리 지연을 초래할 때 문제가 된다.
다음과 같은 반복 루프를 사용하라:
구성, 버전, 환경에 대한 메모를 남겨 나중에 재현할 수 있게 하라.
‘최고 실행’만 골라내거나, 친절한 데이터셋만 선택하거나, 당신의 변경을 돋보이게 하는 단일 지표만 선택해선 안 된다. 그리고 과대 일반화하지 마라: 한 머신이나 벤치마크에서의 승리는 배포 환경, 비용 제약, 사용자 피크 트래픽에선 통하지 않을 수 있다.
헤네시의 지속적 메시지는 실용적이다: 성능은 막연한 기대만으로 확장되지 않는다—적절한 유형의 병렬성을 선택하고 에너지 한계를 존중하며 실제로 중요한 워크로드에 맞춰 최적화할 때 확장된다.
병렬성은 주요 진로지만 결코 ‘공짜’가 아니다. 명령 수준 병렬성, 멀티코어 처리량, 가속기 중 어느 쪽을 추구하든 쉬운 이득은 빨리 소진되고 조정 오버헤드는 커진다.
효율성은 기능이다. 에너지, 열, 그리고 메모리 이동은 피크 GHz 수치보다 먼저 실세계 속도를 제한한다. 전력이나 메모리 한계 내에서 유지할 수 없는 더 빠른 설계는 사용자에게 보이는 이득을 주지 못한다.
워크로드 초점이 일반적 최적화보다 낫다. 암달의 법칙은 시간의 상당 부분이 소비되는 곳에 노력을 쏟으라고 상기시킨다. 먼저 프로파일하라; 그다음 최적화하라.
이 아이디어는 CPU 설계자에게만 해당되는 것이 아니다. 애플리케이션을 빌드하는 경우에도 큐잉, 꼬리 지연, 메모리 압박, 클라우드 비용으로 같은 제약이 나타난다. “공동 설계”를 운영화하는 한 가지 실용적 방법은 아키텍처 결정을 워크로드 피드백에 가깝게 유지하는 것이다: 측정하고 반복하며 배포하라.
예를 들어 채팅 중심 빌드 워크플로우를 사용하는 팀이라면(Koder.ai 같은), 서비스를 빠르게 프로토타입하고 프로파일링과 벤치마크로 병렬성 추구(예: 요청 동시성), 데이터 지역성 개선(예: 왕복 감소, 쿼리 간소화), 또는 특수화 도입(예: 무거운 작업 오프로드)을 결정할 수 있다. 플랫폼의 planning mode, snapshots, rollback 기능은 성능에 영향을 미치는 변경을 점진적으로 시험하는 것을 쉽게 만들어 최적화를 한 번의 되돌릴 수 없는 선택으로 만들지 않는다.
원한다면 비슷한 글들을 더 보고 싶다면 /blog를 둘러보라.