본 게시글은 2진법과 단순한 컴퓨터 지식을 훑어보기 위한 가벼운 목적으로 쓰여졌습니다.
만약 2진법에 대한 자세한 내용을 기대하셨다면 다른 곳에서 공부하시는 것을 추천드립니다.
컴퓨터는 0과 1을 이해할 수 있다
아마 여러분들이 컴퓨터가 잔뜩 나오는 미디어를 보신 적이 있다면, 0과 1이 여기저기서 흘러나오는 연출을 보신 적이 있으실 겁니다.
우리가 한국어를 이해하는 것처럼 컴퓨터는 기계어를 이해할 수 있으며, 기계어는 0과 1로만 이루어져 있습니다.
0과 1만 이해할 수 있는 기계가 어떻게 이렇게 다재다능한 역할을 할 수 있는 걸까요?
그냥 신경쓰여서 홧김에 작성한 게시글이니, 부디 가벼운 마음으로 봐주셨으면 좋겠습니다.
본 게시글은 '컴맹이 컴퓨터와 친해지고 싶어서 작성한 자료'이며, '완벽하게 정확한 교본'이나 '전문적인 용어가 넘쳐나는 자료'는 아니에요!
애초에 저는 머리가 나빠서 그런 자료는 못만들거든요.
아무튼. 숫자 세기부터 시작해볼까요?
10개의 기호로 수를 나타내는 10진법
2진법을 살펴보기 전, 먼저 우리가 잘 알고있는 10진법에 대해 먼저 알아보도록 하겠습니다. 아마 여러분은 '10진법' 이라는 단어를 모르시더라도 256이라는 수를 '이백 오십 육' 이라고 자연스럽게 읽을 수 있으실 겁니다.
백의 자리에 있는 2는 200을 나타내고, 십의 자리에 있는 5는 50을 나타내고, 일의 자리에 있는 6은 6을 나타냅니다.
이처럼 10진법은 이미 우리가 자연스럽게 사용하고 있기 때문에, 이 내용이 크게 어렵게 느껴지지는 않으실 겁니다.
2147483647이라는 큰 수를 읽을 때에도 약간의 시간만 들이면 자연스럽게 읽으실 수 있겠죠.
이처럼 10진법이라는 것은 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 라는 10가지 기호로 수를 표현하는 방법입니다.
1+1?
10진법으로 표현된 수를 통해 몇 가지 간단한 계산을 해보겠습니다. 해당 게시글에서는 여러분이 이미 덧셈을 할 수 있다고 가정하고 진행하겠습니다.
먼저 1+1을 계산해보겠습니다. 1에 1을 더하면 2가 나오게 됩니다!
마찬가지로, 5+5를 하면 5에 5를 더한 수인 10이 나오게 됩니다!
10진법에서 가장 큰 수를 표현하는 기호는 9이며, 5+5는 9보다 큰 수를 가리키기 때문에 '일의 자리'를 벗어나 '십의 자리'에 1이 더해지게 되고(자리올림), 결과적으로 5+5는 10이 되는 것입니다!
같은 원리로, 50+50은 100이 됩니다.
어떤 자리에서 표현할 수 있는 범위를 벗어났기 때문에 다른 자리를 활용해서 수를 나타내게 되는 것이죠.
이처럼 _ _ _ 이라는 3개의 자리가 있다고 가정했을 때, 숫자가 어떤 자리에 위치하느냐에 따라 표현하는 기호는 같다고 하더라도 값이 달라질 수 있습니다!
일반적으로 _ _ _ 이라는 3개의 자리가 있다면 여러분들은 왼쪽부터 '백의 자리', '십의 자리', '일의 자리' 라고 알 수 있으실 겁니다.
하지만 여기서 표현을 조금 바꿔보겠습니다!
백의 자리는 10²의 자리, 십의 자리는 10¹의 자리, 일의 자리는 10⁰의 자리라고 표현하겠습니다.
만약 이러한 표기법이 익숙하지 않으시더라도 너무 걱정하지 마세요!
10²은 100을 나타내고, 10¹은 10을, 10⁰은 1을 나타낸다고 알고 계시면 됩니다.
(하나의 숫자를 다양한 형태로 표현할 수 있다는 점을 이해하시는 것이 중요합니다!)
이러한 표기법에 익숙해지기 위해서, 밑과 지수에 대해 조금 알아보도록 하겠습니다!
- 10 + 10 + 10 = 10 * 3
- 10 * 10 * 10 = 10³
반복적으로 같은 수를 더한다고 가정했을 때, 우리는 1+1+1+1+1 같은 표현 대신 1*5라고 표현할 수 있습니다!
(1이 5개 있다)
이처럼 여러 번 더하는 수를 간단하게 표현하는 이러한 방법을 곱하기라고 합니다.
그렇다면 10*10*10*10*10 처럼 반복적으로 같은 수를 곱하는 경우, 이를 더 단순하게 표현하는 방법도 있을까요?
10*10*10*10*10 과 같이 반복적으로 같은 수를 곱하는 경우, 우리는 이것을 10⁵라고 표현할 수 있습니다!
이러한 표현을 거듭제곱이라고 하며, 10⁵에서 10은 '밑', 5는 '지수' 라고 부릅니다.
해석하실 때는 '밑'을 '지수'번 곱한 값이라고 생각하시면 이러한 표현을 보셨을 때 이해하기 더 수월하실 겁니다!
이제 방금 전 '백의 자리'를 '10²의 자리'로 다르게 표현할 수 있었던 부분을 이해할 수 있으실 겁니다. (10 * 10 = 100)
그런데 어딘가 신경쓰이는 부분이 있지 않으신가요?
10² (10 * 10)은 10을 2번 곱한 100이라고 하고... 10¹이나 10⁰은 대체 뭘까요?
- 10을 1번 곱한다? → 뭘 기준으로 10을 한 번 곱하는 거야?
- 10을 0번 곱한다? → 이건 또 뭐야?
아마 '밑'을 '지수'번 곱한다는 표현만으로는 10¹과 10⁰이라는 표현이 잘 이해가 가지 않으실거라고 생각합니다.
이를 이해하기 위해서 먼저 거듭제곱으로 표현된 수를 서로 곱해보도록 하겠습니다.
10³ * 10³은 10*10*10과 10*10*10을 곱한 값이니 10*10*10*10*10*10으로 쭉 이어서 10⁶ 이라고 표현할 수 있습니다!
10³ * 10³ = 10⁶
이는 이러한 수식으로 정리할 수 있으며, 보시는 것처럼 거듭제곱으로 표현된 수를 서로 곱하는 경우, 밑이 같을 때 지수끼리 더하는 방식으로 표현할 수 있습니다!
10⁵ * 1/10⁵ = 1
짧게 '역수' 라는 재미있는 개념에 대해 알아보도록 하겠습니다.
먼저 10⁵이라는 수가 있다고 가정하겠습니다.
이 수에 어떤 수를 곱하면 1이 나올 수 있을까요?
앞에서 미리 스포일러를 한 것처럼, 1/10⁵를 곱하면 1이 나오게 됩니다.
조금 더 쉽게 받아들이기 위해서 간단한 예제를 살펴보겠습니다.
2라는 숫자가 있다고 가정했을 때, 2를 두 조각(2)으로 정확히 나누면 조각 하나가 1이 될 것입니다.
(여기서 2를 두 조각(2)으로 나누는 것을 2/2라고 표현할 수 있으며, 2를 네 조각(4)으로 나누는 경우는 2/4라고 표현할 수 있습니다)
이 한 조각을 '2를 2로 나눈 것 중 하나' 라고 표현할 수 있으며, 이는 수학적으로 '2 * 1/2'라고 표현할 수 같습니다(2의 절반(1/2)이라고 생각해보세요).
같은 원리로 '3'을 '세 조각으로 정확히 나눈 것 중 한 조각'(1/3)은 1(3 * 1/3)과 같습니다.
(3의 1/3조각)
여기서 여러분은 한 가지 특징을 눈치채셨을 겁니다.
- 2 * 1/2 = 1
- 3 * 1/3 = 1
- n * 1/n = 1
임의의 수 n에 대해, n * 1/n은 항상 1을 나타내게 됩니다!
위의 예제에서, 2에 곱했을 때 1이 되는 수(1/2)를 '2의 역수' 라고 하며, 같은 원리로 n의 역수는 1/n이 됩니다.
(n과 n의 역수를 서로 곱하면 항상 1이 나온다)
이제 10⁵의 역수가 1/10⁵라는 말이 조금은 이해가 되셨을 거라고 생각합니다!
10⁻⁵ = 1/10⁵
자! 10¹과 10⁰을 이해하기 위한 마지막 퍼즐조각입니다!
여러분은 간혹 지수 자리에 음수(0보다 작은 수, -10은 0보다 10 작은 수를 의미한다)가 있는 것을 보신 적이 있으실 겁니다!
대체 지수 자리에 음수가 있는 것은 무엇을 의미하는 것일까요?
간단한 예제로 이해해 봅시다!
10³ = 1000
10² = 100 (1000/10)
10¹ = 10 (100/10)
10⁰ = 1 (10/10)
10⁻¹ = 1/10 (1/10¹)
10⁻² = 1/100(1/10²)
10⁻³ = 1/1000(1/10³)
뭔가 규칙적인 패턴이 눈에 들어오지 않나요?
맞습니다. 지수는 1 증가할수록 밑을 거듭해서 곱하게 되지만, 1 감소할수록 거듭해서 나누게 됩니다!
또한 10⁻¹은 1/10¹, 10⁻²은 1/10²을 나타내는데... 어라? 뭔가 익숙하지 않나요?
10⁻ⁿ = 1/10ⁿ
지수가 -n일 경우, n만큼 거듭제곱한 수의 역수를 나타내게 됩니다!
이제 공부한 내용들을 종합해서 10¹과 10⁰의 정체를 밝혀내봅시다!
앞에서 거듭제곱으로 표현된 수를 서로 곱했던 것을 기억하시나요?
10³ * 10³
= 10⁶
= 10³⁺³
밑이 같은 경우, 지수를 서로 더할 수 있다는 것을 알 수 있었습니다.
이제 지수를 0으로 만들 시간입니다!
10³ * 10⁻³
= 10³ * 1/10³
= 1
= 10⁰
= 10³⁻³
브라보! 10⁰이 1과 같다는 사실을 알아냈습니다!
10⁰ * 10¹
= 10⁰⁺¹
= 1 * 10
= 10
이제 여러분은 10⁰과 10¹이 왜 각각 1과 10을 가리키는지 조금 더 잘 이해하실 수 있으실 겁니다!
여러분은 지금까지의 과정이 단 하나의 문장을 이해하기 위해서였다는 것을 알고 계시나요?
백의 자리는 10²의 자리, 십의 자리는 10¹의 자리, 일의 자리는 10⁰의 자리라고 표현하겠습니다.
10진수를 통해서 수를 다루는 것에 어느정도 워밍업이 되신 것 같으니, 본격적으로 2진법을 알아보도록 합시다!
너무 어렵지 않으니 걱정하지 마세요!
2개의 기호로 수를 나타내는 2진법
2진법은 생소하고 어렵게 보일 수도 있습니다. 한때 저도 그렇게 보였거든요 :P
하지만 '생소'할 뿐이지 어렵지는 않습니다. 오히려 10진법보다 재미있는 부분이 많은 녀석이에요!
10진법이 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 라는 10개의 기호로 수를 표현하는 것처럼, 2진법은 0과 1이라는 2개의 기호로 수를 나타내는 방법입니다!
10진법과 2진법을 번갈아 살펴보면서 이해해보도록 합시다.
10진법에서 5+5를 할 경우, 10⁰의 자리만으로는 표현할 수 없기 때문에 10¹의 자리를 써서 10이라고 표현합니다
7+7을 할 경우, 10⁰의 자리만으로는 표현할 수 없기 때문에(표현 가능한 최댓값인 9보다 큰 수임) 10¹의 자리를 써서(자리올림) 14 (10 + 4) 와 같은 형식으로 표현되는 것입니다.
2진법도 똑같은 원리가 적용됩니다.
여기 _ 라는 빈 자리가 있다고 가정해봅시다. 10진법에서는 사용 가능한 기호가 10개였지만 2진법에서는 2개입니다(0, 1).
2진법에서는 빈 자리 하나에 0이나 1을 넣어서 숫자를 표현할 수 있습니다.
2진법의 세계에 0이라는 수가 있다고 가정하겠습니다.
0에 1을 더해봅시다.
0에 1을 더하면 1...
그렇다면 여기서 다시 1을 더하면 어떻게 될까요?
자리에서 표현 가능한 최댓값(1)을 넘어선 값은 표현할 수 없기 때문에 자리올림이 일어나서 10처럼 보이게 됩니다.
(10이라고 보여서 '십' 이라고 오해할 수 있으나, 1+1=2라는 사실에 주목하자!)
10진법에서는 '2' 였던 수가 2진법에서는 '10'으로 보이게 되는 것입니다.
다시 친숙한 10진법의 세계로 돌아와봅시다.
256이라는 수가 있다고 했을 때, 우리는 이를 '이백 오십 육' 이라고 읽습니다.
256
= 200 + 50 + 6
= 2 * 100 + 5 * 10 + 6 * 1
= 2 * 10² + 5 * 10¹ + 6 * 10⁰
오른쪽에서 왼쪽으로 자리가 올라갈 때마다 자리가 나타내는 값에 10이 곱해지는 것이 보이시나요?
그렇다면 2진법에서 '10' 이라는 숫자가 있다고 생각해봅시다(10진법에서는 '2' 라고 표현합니다).
10
= 2 + 0
= 1 * 2 + 0 * 1
= 1 * 2¹ + 0 * 2⁰
자리가 올라갈 때마다 자리가 나타내는 값에 2가 곱해지게 됩니다!
- 10²의 자리, 10¹의 자리, 10⁰의 자리
- 2²의 자리, 2¹의 자리, 2⁰의 자리
10진법과 2진법의 차이점이 보이시나요?
매번 10이라고 할 때마다 진법이 어떤 것인지 설명하는 것은 번거로우니 이제부터 임의의 숫자 N에 대해 2진법은 N₂, 10진법은 N₁₀이라고 쓰도록 하겠습니다.
익숙하지 않아도 괜찮습니다. 몇 가지 예로 익숙해져봅시다!
1010₂
= 8 + 0 + 2 + 0
= 1 * 2³ + 0 * 2² + 1 * 2¹ + 0 * 2⁰
= 10₁₀
1100₂
= 8 + 4 + 0 + 0
= 1 * 2³ + 1 * 2² + 0 * 2¹ + 0 * 2⁰
= 12₁₀
1001₂
= 8 + 0 + 0 + 1
= 1 * 2³ + 0 * 2² + 0 * 2¹ + 1 * 2⁰
= 9₁₀
위 예제를 어느 정도 살펴보신 뒤, 아래에 있는 연습 문제를 풀어보세요!
1011₂ → N₁₀
1111₂ → N₁₀
0101₂ → N₁₀
10진법과 2진법에 어떤 차이가 있는지 잘 파악하셨다면 어렵지 않게 푸실 수 있으실 겁니다. 만약 풀지 못하셨다면 앞서 설명한 내용들을 다시 살펴보세요!
주어진 수를 자리별로 더하고 최댓값을 넘어서는 값은 자리올림 처리를 합니다.
10진법에서 우리가 흔히 사용하던 방식은 2진법에서도 똑같이 사용됩니다.
1000₂ + 0001₂ = 1001₂ = 8₁₀ + 1₁₀ = 9₁₀
0101₂ + 1010₂ = 1111₂ = 5₁₀ + 10₁₀ = 15₁₀
0011₂ + 0001₂ = 0100₂ = 3₁₀ + 1₁₀ = 4₁₀
0110₂ + 0010₂ = 1000₂ = 6₁₀ + 2₁₀ = 8₁₀
조금만 살펴보시면 금방 아실 수 있으실 겁니다
10진법과 2진법은 수의 표현 방식이 다른 것 뿐이니, 기존에 알고 계시는 사칙연산은 2진법에서 10진법 모두에서 동일한 원리로 수행됩니다!
덧셈에서 자리를 올렸다면 이제 자리를 내려볼까요?
1000₂ - 0001₂ = 0111₂ = 8₁₀ - 1₁₀ = 7₁₀
0110₂ - 0110₂ = 0000₂ = 6₁₀ - 6₁₀ = 0₁₀
1010₂ - 0101₂ = 0101₂ = 10₁₀ - 5₁₀ = 5₁₀
0100₂ - 0011₂ = 0001₂ = 4₁₀ - 3₁₀ = 1₁₀
덧셈과 뺄셈을 하시며 어느 정도 2진수에 익숙해지셨다면 곱셈과 나눗셈도 쉽게 하실 수 있으실 겁니다.
0101₂ * 0011₂ = 1111₂ = 5₁₀ * 3₁₀ = 15₁₀
0010₂ * 0010₂ = 0100₂ = 2₁₀ * 2₁₀ = 4₁₀
1010₂ * 0001₂ = 1010₂ = 10₁₀ * 1₁₀ = 10₁₀
1111₂ * 0000₂ = 0000₂ = 15₁₀ * 0₁₀ = 0₁₀
1111₂ / 0101₂ = 0011₂ = 15₁₀ / 5₁₀ = 3₁₀
1000₂ / 0100₂ = 0010₂ = 8₁₀ / 4₁₀ = 2₁₀
0100₂ / 0001₂ = 0100₂ = 4₁₀ / 1₁₀ = 4₁₀
0000₂ / 1111₂ = 0000₂ = 0₁₀ / 15₁₀ = 0₁₀
시프트(shift) 연산 이라는 것이 있습니다. (shift : 위치를 옮기다, 이동하다)
복잡한 것은 아니니 어려워 하실 필요는 없습니다.
시프트라는 단어가 '이동하다'라는 뜻이 있는 것처럼, 숫자를 통째로 이동시키면 됩니다.
10진수로 간단한 예를 들어보겠습니다. (이해를 돕기 위한 것으로, 실제 계산 결과와는 다릅니다)
<< n : 왼쪽으로 n칸 이동
>> n : 오른쪽으로 n칸 이동
256₁₀ << 1₁₀ = 2560₁₀ (10¹을 곱한 것과 같음)
256₁₀ >> 2₁₀ = 0002₁₀ (10²으로 나눈 것과 같음, 나머지는 버림)
10진법으로 보니 이해가 잘 가시나요? 문자 그대로 숫자를 통째로 '이동' 시켰습니다.
2진법으로 봐도 똑같습니다! (실제 계산 결과는 이렇게 보입니다)
0010₂ << 2₁₀ = 1000₂ = 2₁₀ << 2₁₀ = 8₁₀ (2²을 곱한 것과 같음)
0100₂ >> 1₁₀ = 0010₂ = 4₁₀ >> 1₁₀ = 2₁₀ (2¹으로 나눈 것과 같음)
2진법의 재미있는 점은 논리연산에 있습니다.
기호가 두 개밖에 없다는 점 덕분에 서로가 완전히 상반된 의미(있거나(1), 없거나(0)!)를 갖고 있으며, 그 덕분에 재미있는 연산이 가능합니다!
일반적으로, 1은 참(있음)을 의미하며, 0은 거짓(없음)을 의미합니다. 이 정도만 알면 충분합니다!
하나라도 참이면 무조건 참
OR 이라는 영단어는 '또는' 이라는 뜻을 가지고 있습니다.
예를 들어 선택지가 두 개 있다고 가정했을 때, 둘 중 하나라도 참이면 참이 나온다는 의미입니다(A '또는' B가 참이라면 참).
1₂ | 0₂ = 1₂
0₂ | 0₂ = 0₂
0₂ | 1₂ = 1₂
1₂ | 1₂ = 1₂
1100₂ | 0101₂ = 1101₂
1010₂ | 0001₂ = 1011₂
0110₂ | 1000₂ = 1110₂
0000₂ | 1111₂ = 1111₂
다 참이면 참
AND 라는 영단어는 '그리고' 라는 뜻을 가지고 있습니다.
예를 들어 선택지가 세 개 있다고 가정했을 때, 셋 다 참이면 참이 나온다는 의미입니다(A '그리고' B '그리고' C가 참이면 참).
1₂ & 0₂ = 0₂
0₂ & 0₂ = 0₂
0₂ & 1₂ = 0₂
1₂ & 1₂ = 1₂
1111₂ & 0101₂ = 0101₂
0010₂ & 1000₂ = 0000₂
1101₂ & 0101₂ = 0101₂
0001₂ & 0011₂ = 0001₂
하나만 참이면 참
XOR(eXclusive OR)은 굉장히 재미있는 연산자입니다.
그 이유를 설명하기 전, 위에 '하나만 참이면 참' 이라고 적어두기는 했지만, 솔직히 저는 '다르면 참' 이라는 것으로 이해하고 있습니다. 그냥 개인적으로 그게 더 잘 와닿더라고요.
1₂ ^ 0₂ = 1₂
0₂ ^ 0₂ = 0₂
0₂ ^ 1₂ = 1₂
1₂ ^ 1₂ = 0₂
이제 조금 재미있는 짓을 해보겠습니다.
1010₂ ^ 0001₂ = 1011₂ (A ^ B = C)
1011₂ ^ 0001₂ = 1010₂ (C ^ B = A)
1101₂ ^ 0001₂ = 1100₂ (D ^ B = E)
1100₂ ^ 0001₂ = 1101₂ (E ^ B = D)
위 결과를 잘 살펴보면 A와 B에 대해 배타적 논리합 연산을 수행한 결과가 C인데, C와 B에 대해 배타적 논리합 연산을 수행하면 다시 A가 만들어지게 됩니다.
그저 우연일까요?
이번에는 D와 B에 대해 배타적 논리합 연산을 수행해서 E를 만들었는데, 마찬가지로 E와 B에 대해 배타적 논리합 연산을 수행한 결과 D가 만들어졌습니다!
심지어는 A와 C에 대해 배타적 논리합을 수행하면 B를 알아낼 수도 있습니다!
어떻게 이런 일이 가능한 걸까요?
간단하게 말해서, A와 B에 대해 배타적 논리합 연산을 수행하고 C가 만들어진 시점에서, A와 B는 '서로 어떻게 달라야 하는지'에 대한 정보를 알고 있는 것이며, C는 '서로 얼마나 다른지'에 대한 정보를 알고 있게 됩니다. 따라서 '얼마나 다른지' 알고 있다면 서로 다른 데이터를 '복원' 하는 것도 가능할 것입니다.
크... 진짜 너무 신기하지 않나요?
반전
NOT은 아주 간단합니다.
청개구리 같은 녀석으로, NOT이 붙으면 상태가 반전됩니다.
!1₂ = 0₂
!0₂ = 1₂
!1101₂ = 0010₂
!1111₂ = 0000₂
!1010₂ = 0101₂
!0000₂ = 1111₂
NOT ( A OR B ) = ( NOT A ) AND ( NOT B )
NOT ( A AND B ) = ( NOT A ) OR ( NOT B )
드모르간 법칙은 논리 연산에서 논리합은 논리곱과 부정기호로, 논리곱은 논리합과 부정기호로 표현할 수 있음을 가리키는 법칙입니다.
더 간단하게 말해서, OR 없이 OR 연산을 할 수 있으며, AND 없이 AND 연산을 할 수 있다는 것입니다.
한 번 살펴봅시다!
! ( A | B ) = ( ! A ) & ( ! B )
- ! ( 1 | 0 ) = ( ! 1 ) & ( ! 0 ) = 0 & 1 = 0
- ! ( 0 | 1 ) = ( ! 0 ) & ( ! 1 ) = 1 & 0 = 0
- ! ( 0 | 0 ) = ( ! 0 ) & ( ! 0 ) = 1 & 1 = 1
- ! ( 1 | 1 ) = ( ! 1 ) & ( ! 1 ) = 0 & 0 = 0
! ( A & B ) = ( ! A ) | ( ! B )
- ! ( 1 & 0 ) = ( ! 1 ) | ( ! 0 ) = 0 | 1 = 1
- ! ( 0 & 1 ) = ( ! 0 ) | ( ! 1 ) = 1 | 0 = 1
- ! ( 0 & 0 ) = ( ! 0 ) | ( ! 0 ) = 1 | 1 = 1
- ! ( 1 & 1 ) = ( ! 1 ) | ( ! 1 ) = 0 | 0 = 0
쭉 보다보면 정말 OR 연산 없이 OR 연산을 구현했으며, AND 연산 없이 AND 연산을 구현했다는 것을 알 수 있습니다.
하지만... 뭔가 부족하다는 생각이 들지는 않으신가요?
OR 연산 없이 OR 연산을 구현했고, AND 연산 없이 AND 연산을 구현했다면... 그렇다면 XOR도 가능하지는 않을까요?
한 번 시도해봅시다!
A XOR B 를 XOR 없이 구현하시오.
바로 정답을 적어봅시다.
NOT ( A AND B ) AND ( A OR B ) = A XOR B
뭔가 복잡해 보이시나요?
아마 대부분의 경우에는 진리표를 활용해서 맞다는 것을 증명하겠지만... 한 번 직접 말로 설명해 보도록 하겠습니다.
개인적으로 그 편이 훨씬 이해하기 쉬웠거든요!
A와 B가 둘 다 참이 아닌 동시에 A와 B 둘 중 하나라도 참이라면 A와 B는 서로 다르다.
'A와 B가 둘 다 참이 아닌 동시에'라는 부분에서 '1 AND 1'은 걸러지게 됩니다. 그리고 뒤이어 등장한 조건인 'A와 B 둘 중 하나라도 참이라면' 이라는 부분에서는 '0 OR 0' 조건도 걸러지게 되죠. 따라서 남은 '1 OR 0' 또는 '0 OR 1' 조건 에서만 참이 나오게 되면서 성공적으로 XOR 연산을 구현할 수 있게 됩니다.
중요한 부분은 '어떤 논리를 표현하고 싶은가' 이지 외우는 것이 아닙니다.
이 점을 꼭 명심해주세요.
저는 머리가 좋지 않아 이것들을 전부 외우려고만 했었는데... 썩 좋은 기억은 아니었습니다.
안좋은 경험을 하는 사람은 저 한 명으로 족하니까요.
16개의 기호로 수를 나타내는 16진법
16진법도 마찬가지로 수를 표현하는 기호가 16개라는 것이 특징입니다.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
이렇게 16개의 기호로 수를 표현할 수 있으며 한 자리에 0~F(0~15)까지의 숫자를 담을 수 있습니다.
2진법을 아시고 계신다면 엄청나게 빠르게 습득하실 수 있으실테니, 너무 겁먹지 마세요!
자. 16진법에서는 한 자리에 0~15까지 총 16개의 숫자를 담을 수 있습니다.
왜 하필 16개일까요?
0000₂ = 0₁₀ = 0₁₆
0001₂ = 1₁₀ = 1₁₆
0010₂ = 2₁₀ = 2₁₆
0011₂ = 3₁₀ = 3₁₆
0100₂ = 4₁₀ = 4₁₆
0101₂ = 5₁₀ = 5₁₆
0110₂ = 6₁₀ = 6₁₆
0111₂ = 7₁₀ = 7₁₆
1000₂ = 8₁₀ = 8₁₆
1001₂ = 9₁₀ = 9₁₆
1010₂ = 10₁₀ = A₁₆
1011₂ = 11₁₀ = B₁₆
1100₂ = 12₁₀ = C₁₆
1101₂ = 13₁₀ = D₁₆
1110₂ = 14₁₀ = E₁₆
1111₂ = 15₁₀ = F₁₆
위 예시를 보시면 4자리의 2진수는 2⁴ (16, 0~15)가지 경우의 수가 있습니다.
그리고 16진수 한 자리에는 16(2⁴)개의 기호가 들어갈 수 있습니다.
예시를 하나 더 보시겠습니다.
10110101₂ = 1011 0101 = B5₁₆
10010110₂ = 1001 0110 = 96₁₆
11111111₂ = 1111 1111 = FF₁₆
잘 보시면 8자리의 2진수를 4개 단위로 끊어서 2글자로 만들 수 있다는 것을 보실 수 있습니다.
자. 생각해봅시다.
2진수는 0과 1로만 숫자를 표현할 수 있기 때문에, 자릿수가 많아진다면 보자마자 숫자를 알아내는 것이 어려울 수 있습니다.
예를 들어, 만약 여러분이 1111110010111011₂라는 숫자를 보신다면 이를 10진수로 변환하는 것은 상당히 귀찮을 것입니다. 자릿수가 여기서 더 늘어난다면 더더욱 그러겠죠.
여기서 16진수를 사용한다면 이런 2진수를 보다 직관적으로 파악할 수 있습니다. 자리값을 찾을 필요도 없이, 4개씩 끊어서 변환하면 되기 때문입니다.
앞에서 말한 1111110010111011₂를 16진수로 변환해서 직관적으로 보이게 해보겠습니다.
16진수는 보통 0xN의 형태로 표현하기 떄문에, 이제부터 16진수는 0xN 형태로 표현하겠습니다.
(0x의 x는 heXadecimal(16진법)을 의미합니다)
1111110010111011₂ = 1111 1100 1011 1011 = 0xFCBB
1111110010111011₂이 아주 간단하게 줄여졌습니다.
2진법을 사용하는 컴퓨터의 특성상 10진법 보다는 2진법에 친숙한 진법을 사용하는 것이 좋은데, 대표적인 것이 바로 이 16진법 입니다.
참고 : 2진수 하나(0 또는 1)를 담을 수 있는 크기를 1비트라고 하며, 2진수 8개를 담을 수 있는 크기를 8비트 또는 1바이트라고 한다. 따라서 11111111₂ = 0xFF 는 서로 1바이트로 크기가 같다.
Hellp; Wurld?
컴퓨터와 2진법의 관계에 대해 간단하게 알아봅시다.
컴퓨터라는 친구는 간단히 말해 계산기입니다.
컴퓨터의 먼 조상이 주판이라는 것만 봐도 알 수 있는 사실이죠.
당장 인류 역사에서 얼마나 많은 계산이 사용되었는지 하나 하나 세는 것은 불가능합니다.
계산은 정말 많은 분야에서 필수적인 요소이며, 컴퓨터가 빠른 속도로 발전한 것도 바로 이 엄청난 수요 때문이었습니다.
(예전에는 계산을 전문으로 하는 직업의 이름이 컴퓨터였다고 합니다!)
컴퓨터가 2진법을 사용하는 이유는 컴퓨터가 계산기이기 때문입니다.
수를 구별한다고 가정해봅시다.
만약 컴퓨터가 우리와 같은 10진법을 사용한다면 어떨까요?
컴퓨터가 어떠한 물리적 요인으로 인해 간섭을 받는다면 계산 결과에 '오차'가 생길 수도 있을 것입니다.
정확환 값을 요구하는 환경에서, 이러한 오차는 엄청나게 치명적인 결과를 만들 수도 있겠죠.
하지만 컴퓨터가 2진법을 사용한다고 생각해봅니다.
수를 구별하는 '판정 기준'은 딱 한 가지 밖에 없습니다.
전기가 들어오고 있거나 (1)
전기가 들어오고 있지 않거나 (0)
만약 컴퓨터가 이렇게 명확한 판정 기준으로 수를 세게 된다면, 오차가 생기는 것은 상상하기 어려울 것입니다.
Hello, World!
(물론 이와 관련한 자세한 내용이 많지만 여기서는 생략하겠습니다)
이처럼 컴퓨터가 2진법을 사용하는 이유는 본래의 목적에 충실한 '정확한 연산'을 위함입니다.
이제 충격적인 사실을 하나 이야기 해보겠습니다.
앞서 2진법의 논리 연산에 대해 다루었던 것을 기억하시나요?
컴퓨터는 그런 단순한 논리가 엄청난 밀도로 서로 얽혀있습니다!
NOT 연산을 끊임없이 반복하면서 컴퓨터에게 '시간 감각'(0과 1의 주기)을 갖게 해주는 논리가 있으며, '데이터를 저장'할 수 있도록 하게 해주는 엄청난 논리도 존재합니다.
그야말로 컴퓨터는 논리의 결정체인 것이죠!
지금은 이 정도만 알면 충분합니다. 이제 다음 내용으로 넘어가봅시다!
해석의 차이
우리는 앞에서 10진법, 2진법, 16진법에 대해 알아보았으며, 컴퓨터가 어째서 2진법을 사용하는지에 대해서도 간단하게 알아보았습니다.
앞에서 컴퓨터는 '계산기' 라고 알아보았습니다. 하지만 우리가 생각하는 일반적인 계산기는 '숫자'를 입력받아서 '숫자' 결과를 반환하는 것일 겁니다.
하지만 컴퓨터는 어떤가요?
숫자는 물론이거니와 많은 문자들을 이해할 수 있을 뿐더러, 심지어는 영상이나 음악과 같은 데이터도 다룰 수 있습니다!
0과 1만을 이해하는 컴퓨터가 어떻게 이럴 수 있는 걸까요?
답은 간단합니다.
'이렇게 표현된 숫자는 이렇게 해석하기로 하자' 라고 약속을 해두면 됩니다.
그렇다면 컴퓨터는 '숫자'를 연산하면서 많은 데이터 형식을 다룰 수 있게 됩니다!
예를 들어, A~Z는 65~90으로, a~z는 97~122로 하자고 약속을 한다면 컴퓨터가 어떤 수를 원하는 형태로 표시하게 할 수 있습니다.
0과 1이 컴퓨터에 어떻게 담겨있는가는 중요하지 않습니다.
중요한 것은 0과 1을 '어떻게 해석하느냐'입니다.
해석하기에 따라 그것은 어떤 데이터라도 될 수 있습니다.
만약 올바르게 해석하지 않는다면 예상과는 전혀 다른 결과물을 볼 수도 있겠죠.
컴퓨터는 정확한 입력을 받아야만 정확한 결과를 내보낸다는 점을 잊지 마세요.
helloworld.txt
여러분들은 컴퓨터를 사용하시면서 파일의 끝부분에 .txt나 .png, .mp4, .mp3처럼 어떤 특징적인 접미사가 붙어있는 것을 보신 적이 있으실 겁니다.
만약 보이지 않는다면 파일 탐색기(아무 폴더나 여셔도 상관 없습니다 :D)를 열고 '보기' 탭에 가셔서 '파일 확장명' 옵션을 활성화 해주세요!
그럼 이제 많은 파일들이 확장자를 갖고 있는 것을 볼 수 있으실 겁니다!
'확장자' 라는 것은 '파일의 종류를 구분하는 데 쓰이는 기준'입니다.
간단히 말해 '확장자'란 '데이터를 이런 형태로 해석해라' 라는 명시적인 표시인 것이죠!
예를 들어, 그림 파일의 확장자에는 .jpg, .png, .bmp 등과 같은 확장자가 붙어 있는데, 컴퓨터는 이런 확장자를 가진 파일을 보고 '이건 그림이구나' 라고 알게 되는 것과 같습니다!
이런 파일의 확장자를 바꾸지 못하는 고유한 값이라고 생각하실 수도 있겠지만, 사실은 전혀 아닙니다!
파일에 마우스를 가져다 대신 다음 '우클릭'을 하고 '이름 바꾸기' 옵션을 눌러보세요!
아마 확장자를 변경하실 수 있으실 겁니다.
만약 여러분이 '그림' 파일의 확장자를 .txt로 바꾸고 열게 된다면, 엄청나게 이상한 글자로 가득 찬 텍스트 문서를 보게 되실 겁니다!
대체 이게 무슨 말인 걸까요?
당황하지 마세요! 이것은 '이미지 형식'으로 저장된 파일을 '텍스트 형식'으로 해석해서 이상하게 보이는 것입니다!
잎에서 데이터는 '해석의 차이' 라고 언급했었습니다.
만약 우리가 이런 괴상망측한 형태가 아닌 '순수한 파일'을 보고 싶다면 어떻게 해야 할까요?
이렇게 망가진 모습이 아닌 '수'로 표현된 모습을 보고 싶다면?
방법은 많지만, 오늘은 그중 HxD라는 프로그램을 사용해보겠습니다!
파일의 실체
HxD라는 프로그램은 16진수로 표현된 이진파일(Binary file)을 읽을 수 있게 쉽게 보여주는 프로그램으로, 심지어는 파일 내부의 16진수 값을 직접 수정할 수도 있습니다!
먼저, 여기서 한국어 버전의 HxD를 다운로드 받아보겠습니다!
다운로드를 다 받고 프로그램을 실행하신다면 이런 화면이 가장 먼저 여러분을 반길 것입니다.
와... 뭐랄까 엄청나게 텅 비어있네요.
여기서 파일을 열려면 상단에 있는 '파일(F)' 버튼을 누르고 '열기(O)' 옵션을 클릭한 다음 보고 싶은 파일을 선택하시면 됩니다!
저는 방금 전 처참하게 깨져있었던 txt 파일을 열어보겠습니다... (확장자를 임의적으로 변경했던 그 파일입니다!)
16진수를 알고 계신다면 무서워 하실 필요가 없습니다!
일단 16진수가 두 개 씩 짝지어서 있는 걸 보니, 1바이트씩 나눈 것처럼 보이네요.
바로 이 16진수들이 '이미지'(정확히는 .png) 파일의 실체입니다!
분명 이 16진수들이 2진법 형식으로 컴퓨터 어딘가에 저장되어 있을 것입니다...
자. 앞에서 데이터는 '해석의 차이' 라고 했습니다.
이미지가 PNG 형식으로 컴퓨터에 저장이 되고 해석이 되기 위해서는, 분명 그에 맞는 형식이 있어야만 할 것입니다.
한 번 직접 찾아봅시다!
Q. PNG 파일의 헤더 시그니처 (매직 넘버)는 무엇인가요?
위 문제를 해결하기 위해 검색을 하셨다면 'PNG 파일 구조'에 대해 찾아 보실 수 있으셨을 거라고 생각합니다.
파일 구조를 모두 이해하지 못했다고 해도 문제는 없습니다!
'파일은 형식(포맷 이라고도 합니다!)에 따라 특정 위치에 파일의 특징적인 정보를 담은 16진수 값이 존재한다' 라는 맥락을 이해하셨다면 그거로 충분합니다!
어디에 숨긴거야?
이번에는 '파일마다 특징적인 구조가 존재한다'는 사실을 바탕으로 파일에 정보를 숨겨보도록 합시다!
먼저, 간단한 상상을 해봅시다.
여러분이 어떤 기업의 산업 스파이라고 가정하겠습니다.
당신은 중요한 설계도면을 몰래 훔칠 방법을 모색하던 중, '파일 속에 파일을 숨길 수는 없을까?' 하는 생각을 품게 되었습니다.
예를 들어, 이미지를 더블클릭해서 열면 평범한 거위 사진이 나오지만 그 실체는 중요한 정보를 품은 황금알을 낳는 거위였던 거죠!
하지만 어떻게 파일 속에 정보를 숨길 수 있을까요?
이제 '스테가노그래피'의 세계에 빠져볼 시간입니다!
스테가노그래피(Steganography) : "감추어져있다" (Stegano) + "쓰다, 그리다" (graphos)
스테가노그래피는 보이지 않는 곳에 정보를 숨기는 은닉법이라고 할 수 있습니다!
스테가노그래피의 매력에 빠지실 수 있도록 두 가지 예제를 준비했습니다!
파일의 구조를 보지 못한다면 절대 못찾아낼거야.
첫 번째 예제를 보기 전, 간단한 생각을 해봅시다.
당신은 컴퓨터고, 파일을 열어달라는 요청을 받았습니다.
파일은 PNG 형식이었고, 당신은 'PNG 파일의 특징적인 구조'를 찾아서 그 내용을 해석한 다음 사용자에게 보여줄 것입니다.
'PNG 파일의 구조'에는 '나는 PNG 파일이야' 라는 정보를 담은 16진수 값이 있을 것이고, '여기까지가 나야!' 라는 정보를 담은 16진수 값이 있을 것입니다.
그렇다면 만약 파일에 '여기까지가 나야!' 라고 한 뒷부분에 무언가가 적혀 있으면 어떻게 될까요?
컴퓨터는 PNG파일의 끝으로 취급되는 부분까지만 읽고 뒷내용은 전혀 신경쓰지 않을 것입니다!
첫 번째 스테가노그래피 예제는 바로 그 부분을 노렸습니다!
먼저, 정보를 숨겨놓을 파일과 숨길 파일을 선택해줍니다.
저는 확장자를 바꿨던 파일의 확장자를 다시 .png로 돌려 놓고, 이 파일에 정보를 숨겨보도록 하겠습니다.
숨길 파일은 바로 이 파일입니다.
정보를 숨겨놓을 파일을 HxD에서 열어보겠습니다.
검색을 해서 찾은 정보에 따르면, 이 부분이 '나는 PNG 파일이야' 라는 부분에 해당하는 '헤더 시그니처' 인 것을 알 수 있습니다. 그렇다면 이제 '여기까지가 나야!' 라고 말하는 부분을 찾아봅시다.
검색 결과에 따르면 바로 이 부분이 PNG 파일의 끝을 나타내는 '푸터 시그니처' 라고 합니다.
그렇다면 이제 정보를 숨길 시간입니다!
먼저, 숨길 파일을 HxD에서 열어준 다음 내용을 확인합니다!
와우... 엄청나게 짧네요. 역시 그냥 텍스트 파일이라 그런지 내용도 바로 옆에서 잘 확인할 수 있습니다.
16진수 아무 곳이나 클릭한 다음에 Ctrl + A를 눌러서 전체를 선택한 다음 Ctrl + C로 복사해줍시다.
그 다음, 다시 정보를 숨겨놓을 파일로 돌아와서 푸터 시그니처 끝을 클릭한 다음 Ctrl + V로 붙여넣어줍시다!
'파일 크기를 변경합니다.' 라는 경고가 나오네요. 수락을 눌러줍시다.
성공적으로 내용을 붙여넣었으니 Ctrl + S를 눌러서 저장을 해줍니다.
이제 file.png 파일을 열어보겠습니다.
달라진 부분은 딱히 없네요.
하지만 이 이미지를 다시 HxD에서 열어보겠습니다.
성공적으로 정보가 감춰진 것을 확인할 수 있습니다!
만약 여러분이 '텍스트 파일'이 아닌 '바이너리 파일'을 숨기고 싶으시다면, 파일 전체를 푸터 시그니처 뒤에 붙여넣으시면 됩니다.
그렇다면 반대로 꺼낼 때에는 어떻게 해야 할까요?
먼저 푸터 시그니처 뒤에 파일을 숨겼으니, 파일의 푸터 시그니처를 찾아봅시다.
Ctrl + F를 누르고 '찾기'창을 띄우신 다음, 다음과 같이 데이터 형식을 '16 진수 값'으로 설정하고 선택된 부분에 푸터 시그니처를 입력하고 검색하시면 됩니다!
(숨긴 파일의 헤더 시그니처를 찾는 방법을 선택하셔도 됩니다!)
이렇게 '숨겨진 파일'에 해당하는 부분을 찾으셨다면, 그 부분을 전부 복사한 다음 HxD에서 '파일(F)' 버튼을 누르고 '새로(N)' 버튼을 누르신 다음 복사한 파일의 내용을 붙여넣고 '파일(F)'에 들어가셔서 '다른 이름으로 저장(A)' 을 누르신 다음 원하시는 곳에 적절한 확장자를 써서 저장하시면 됩니다!
파일의 구조를 이해하지 못한다면 절대 못찾아낼거야.
자! 두 번째 예제는 조금 더 소름끼칠 겁니다!
먼저, 그림판을 열어줍니다.
그리고 그림판 바닥에 아무 글자나 적어서 다른 이름으로 저장한 다음, HxD에서 해당 파일을 열어줍니다.
헤더 시그니처와는 다른 부분에 강조를 해둔 것이 보이시나요?
각각의 칸은 '이미지의 너비와 높이'를 의미합니다.
(저는 여기서 관련 내용을 찾을 수 있었습니다!)
높이(두 번째 네모)의 값을 직접 00 00 00 30으로 바꿔서 높이를 줄인 다음 저장해봅시다!
뭔가 이상한 이미지네요. 지금 이 이미지는 특별한 파일을 숨긴 것도 아니기 때문에, 시그니처를 찾아보려고 해도 아무런 성과를 거둘 수 없을 것입니다.
파일의 구조를 '변형' 시켜서 의도적으로 보지 못하도록 만든 것입니다!
소름끼치지 않나요?!
다시 HxD를 열고 이 이미지의 높이에 해당하는 부분을 원래대로 돌려놓아봅시다!
그리고 저장을 한 다음 이미지를 다시 열어보면...?
아하! 이게 숨겨진 메시지였군요!
처음 스테가노그래피로 정보를 숨기셨을 때, HxD가 '파일 크기를 변경합니다.' 라는 경고문을 보여줬던 것을 기억하시나요?
이러한 스테가노그래피 기법을 통해서 정보를 숨기려고 해도, 만약 그 정보가 거대한 것이라면 결국 '내용에 비해 비정상적으로 큰 용량' 때문에 발각이 될 수도 있다는 점을 유의하세요!
안녕하세요!
여러분이 이 글을 통해서 2진법과 컴퓨터에 조금 더 친해지셨으면 좋겠다는 바람으로 이렇게 글을 작성해보았습니다!
0과 1로 이루어진 컴퓨터의 세계에 조금이라도 익숙해 지셨을까요?
만약 그러셨다면 저는 더이상 바랄게 없습니다!
만약 컴퓨터와 조금 더 친해지고 싶으시다면, 컴퓨터 구조를 조금 공부하신 다음에 프로그래밍을 공부하시는 것을 추천드려요!
저도 프로그래밍을 공부하면서 컴퓨터와 친해질 기회를 많이 가질 수 있었기 때문에, 여러분들도 부디 더 컴퓨터와 친해지셨으면 좋겠어요!
그럼 언젠가 다음 게시글에서 만나기를 고대하며 이만 글을 마치도록 하겠습니다!
긴 글 읽어주셔서 정말 고마워요!