도대체 이번 병렬 컴퓨터 시리즈는 끝날 생각을 안 하는 것 같군요. 원래는 한번에 끝내려고 했던 것인데 벌써
4회째입니다. 그리고 지금까지의 추세로 보아서는 이번 회에도 끝이 나기는 힘들어 보입니다. 그야말로 몇 줄 안되는
코드 가지고 A4 10장씩 5회 이상을 끄는 것을 보면 과연 프로그래밍 언어라는 것이 ? 그 코드에 대한 설명이라는
것이 ? 지금까지 얼마나 허술하게 해설되어 왔던가를 새삼 느낄 수 있습니다.
이곳의 병렬 컴퓨터 정도의 코드라면 일반적으로는 애당초의 저의 의도대로 단 한번에 그 설명을 마치는 것이 대부분의
인터넷 강좌 내지 데브피아 같은 유명 사이트의 관행이었습니다. 초보를 상대로 하는 강좌의 경우에도 대개는 인정사정
없습니다. 그러다 보니 늘상 달리는 댓글이,
“그대로 따라했는데 안되요. ㅠㅠ;;”
이런 식입니다. 그런 댓글이 달려 있는 코드를 제가 직접 따라해 보아도 역시 안되는 경우가 부지기수입니다. 이는
그 해설을 한 사람에게 문제가 있습니다. 그런 식으로 할 바에야 안 하는 것이 낫습니다. 괜시리 남들 호기심만 끌어
놓고 그들을 골탕만 먹이는 일이기 때문입니다. 많은 해설자들이 반쯤 설명하다 만 것을 가지고 스스로 명강좌를 했다고
착각합니다. 이는 한국에서나 가능한 일이며 미국이나 일본 같은 곳에서는 즉각 천민 취급을 당하게 됩니다.
명령을 받는 개별 컴퓨터의 프로그램 코드를 다시 한 번 예시하면, |
|
Imports
System
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Public Class Monster_individual
|
Inherits
System.Windows.Forms.Form
|
#Region "
Windows Form 디자이너에서 생성한 코드 " 어쩌고
저쩌고 …..
#End Region
|
Dim indi_Listener As TcpListener
Dim indi_socket As Socket
Dim indi_Stream As NetworkStream
Dim strReader As StreamReader
|
Private Sub
indi_start_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
_ |
Handles indi_start.Click
|
Dim ip_address
As IPAddress = IPAddress.Parse(ip.Text)
Dim port As Integer = CInt(po.Text)
indi_Listener = New TcpListener(ip_address, port)
indi_Listener.Start()
Dim receive_Thread_sub As New Thread(AddressOf receive_Thread)
receive_Thread_sub.Start()
|
End Sub
Public Sub receive_Thread()
|
Dim command_string
As String
Dim ecd_Encode As Encoding = Encoding.GetEncoding("KS_C_5601-1987")
While True |
indi_socket
= indi_Listener.AcceptSocket
indi_Stream = New NetworkStream(indi_socket)
strReader = New StreamReader(indi_Stream, ecd_Encode)
command_string = strReader.ReadLine()
If Not (command_string Is Nothing) Then |
TextBox1.Text
&= command_string
|
End If
|
End While
|
End Sub
|
End Class
|
|
|
입니다.
별도의 윈도우 폼을 따로 열어서 만들었기 때문에 새로운 클래스로 만들어지게 되었습니다. 하지만 반드시 그럴 필요도
없고 하나의 폼에 2개의 클래스를 만들 수도 있으며 아예 1개의 클래스 안에 명령을 하는 컴퓨터와 명령을 받는 컴퓨터의
코드를 함께 기재할 수도 있습니다. 맨 마지막의 경우는 하나의 클래스 안에 명령하는 컴퓨터에서 쓰는 버튼과 명령을
받는 컴퓨터에서 쓰는 버튼이 각각 하나씩 있으면 그만입니다.
명령을 하는 컴퓨터는 그 중 자기에게 속한 버튼 하나만 쓰면 되고 명령을 받는 컴퓨터 역시 마찬가지입니다. 어렵진
않은 내용이지만 초심자의 경우 즉각 이해가 가지 않을 수도 있습니다. 하지만 이 부분은 그다지 핵심적인 부분은 아니기에
(괜히 말을 꺼낸 격이라고도 할 수 있습니다) 잊어버리고 넘어가도 좋습니다. |
|

|
|
어쨌든 위의 코드가 명령을 받는 컴퓨터에서 쓰이는 코드입니다. 이 코드를
담은 실행파일을 그 컴퓨터의 아무 곳(예를 들면 바탕화면)에다가 떨어뜨려 놓고 실행시키면 됩니다. 실행파일을
다루는 방법은 나중에 코드 설명이 다 끝난 후에 상세히 설명 드립니다.
Imports System
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
명령을 하는 컴퓨터 코드에서 이미 설명을 했습니다. 반드시 같은 네임스페이스를 임포트해야 하는 것은 아닙니다.
|
|
|
맨 나중의 System.Threading 네임스페이스는 사실 이전의 명령을 하는 컴퓨터에서는 필요 없는 것이었습니다.
하지만 이곳에서는 반드시 필요합니다. 통상 가려내기 귀챦아서 왠만한 것들은 Ctrl+V 해 버리다 보니 이런 일이
많습니다.
Public Class Monster_individual
명령을 받는 컴퓨터의 이름이 Monster_individual 로 정해졌습니다. 각 개별 컴퓨터라는 뜻으로 individual
이라는 단어를 사용했습니다. 이처럼 변수나 객체, 그리고 클래스 등의 이름은 가급적 풀네임을 써 주는 것이 절대적으로
유익합니다. 나중에 대형 프로그램을 짜다 보면 그럴 필요를 절실히 느끼게 됩니다.
언더바를 여러 개 사용해서 거의 문장을 만들다시피 변수 이름을 짓는 것이 요즘 저의 프로그래밍 습관입니다. 변수
그 자체만 보고도 이것이 어떤 변수이고 따라서 그것을 포함하는 코드가 무슨 뜻인지를 알 수 있어야 합니다. 즉, 코드
그 자체가 하나의 영문 주석이 되어야 합니다.
또 한가지 프로그래밍 습관에 관해서 말하자면 프로그램 그 자체는 당당해야 한다는 점입니다. 무슨 말이냐 하면 체계성과
정규성을 갖춘 프로그램이어야 하며 임기응변은 금물입니다. 물론 그때그때마다의 얄팍한 임기응변으로 순간 순간의 땡처리를
해 나갈 수도 있겠고 그렇다고 해서 프로그램이 안 돌아가는 것은 아닙니다. 프로그램은 씽씽 잘만 돌아갑니다. 물론
처음에만 말입니다.
|
|
버그 없는 프로그램 없고 나중에 수정하지 않게 되는 프로그램 역시 없습니다.
바로 이 순간 임기응변식 땡처리의 대가를 치르게 됩니다.
먼저 일단 기억이 안 납니다. 내가 만든 코드임에도 도대체 어떻게 만든 것인지 한참을 되새겨 보아야 합니다.
그러고 나면 이제 가물가물하게 기억이 재생되는데 그런 국면이 프로그램 이곳저곳에 산재하다 보면 다음과 같은
문제가 생깁니다.
즉, 어느 한 부분의 코드를 고치게 되면 그것이 다른 부분들에 대해 어떤 영향을 미치게 되는지를 알 수
없게 됩니다. 결국 모든 코드를 통째로 복습해서 이해한 다음에야 코드 한 줄을 수정할 수 있다는 이야기가
됩니다.
이렇게 되면 그 프로그램은 게임 끝입니다. 수정 불가입니다. 아무도 못 고칩니다. 만든 사람부터 못
고치는 프로그램을 그 어느 누가 고칠 수 있을까요. 이것이 지금까지 수 많은 기업에서 프로그램의 유지 보수
문제가 심각했던 대표적인 이유입니다.
|
|
 |
|
|
적어도 프로그램을 만든 사람은 자신이 만든 코드는 즉각적으로 읽고 이해할 수 있어야 합니다. 그러기 위해서는
코드 자체가 전체적으로 체계성이 있어야 합니다. 당당한 코드이어야 하며 왜곡된 코드이어서는 안됩니다. 임기응변식 코드는
왜곡된 코드입니다. 같은 임기응변이라도 멋진 코드가 있을 수 있으며 이 경우는 왜곡이 아닙니다. 멋진 코드입니다.
하지만 천재가 아닌 이상 그런 멋진 코드가 계속적으로 머리에서 떠오를 리는 만무합니다. 또한 그런 천재적인 임기응변식
코드라도 지속적으로 등장하게 되면 역시 혼란스러워지게 됩니다. 따라서 그런 코드는 한 두번 이상 써먹으면 안됩니다.
원칙성, 체계성, 정규성 등의 개념에 입각한 코드가 당당한 코드입니다. 한마디로 대쪽 같아야 합니다. 앞으로 프로그램을
짜 나가다 보면 그런 개념들의 의미를 점차적으로 느끼게 될 것입니다.
Inherits System.Windows.Forms.Form
이 상속 코드는 이전에 설명이 미흡했기에 여기서 보충 설명을 드리고는 싶지만 저 스스로부터가 상속에 관한 지식이
짧습니다. 또한 이 코드는 상속을 설명하기에는 매우 부적당한 코드입니다. 따라서 여기서는 상속에 관해 최소한의 개념
정립을 할 수 있는 별개의 초간단 코드 몇 줄만 본 강좌가 끝나는 부분에서 부록으로 해설해 드리겠습니다. |
|
 |
|
다만 여기서는 이곳의 클래스, 즉,
“Monster_individual 클래스가 System.Windows.Forms 네임스페이스의 Form
클래스를 상속하고 있다, 그래서 Form 클래스의 변수나 멤버들(프로퍼티와 메소드)는 마치 당해 클래스
내에서 사용하듯이 - 하등의 새로운 변수 선언 과정의 필요 없이 - 이 클래스 내에서 그대로 사용할 수
있다는 것을 의미하고 있다”
는 정도만 언급하고 넘어갑니다. 나중에 다시 정확히 설명드릴 것이니 지금은 잊어 버리셔도 좋습니다.
Dim indi_Listener As TcpListener
indi는 individual의 약자입니다. 그냥 individual이라고 쓸 것을 그랬나 봅니다. 대형
프로그램을 짤 경우에는 반드시 그렇게 해야 합니다. 이곳에서는 한 페이지 밖에 안되는 프로그램이므로 약자를
사용했습니다.
|
|
|
TcpListener가 하는 역할은 TcpClient로부터 전송되어져 오는 정보를 받아들이는 일입니다. 하지만 이
정보를 받는 과정은 단순하지가 않아서 그 사이에 여러 단계의 처리 과정을 거치게 됩니다. 그 과정에서 수 많은 객체나
메소드 등을 정신 사나울 정도로 혼란스럽게 사용하게 되는데 이것이 바로 객체제향 프로그래밍의 어려운 부분입니다. 이런
것들만 자유자재로 구사할 수 있다면 그 사람은 프로그래밍의 지존으로 등극한다 해도 무리한 표현은 아닙니다. 아래의 선언들이
바로 그런 처리 과정을 위한 준비라고 보면 되겠습니다.
Dim indi_socket As Socket
Dim indi_Stream As NetworkStream
Dim strReader As StreamReader
소켓 클래스로부터 소켓 객체 하나를 선언했고, 역시 네트워크스트림 클래스와 스트림리더 클래스로부터 각각 객체 하나씩을
준비시켰습니다. 감자탕을 끓이기 위해 감자 뼈다귀 야채 등을 준비하는 과정이나 진배 없습니다.
Private Sub indi_start_Click(ByVal sender
As System.Object, _
ByVal e As System.EventArgs) Handles indi_start.Click
다시 설명할 필요도 없는 버튼이벤트 생성 코드입니다. 이미 말씀드린 것처럼 이러한 이벤트 코드는 대부분 자동생성되기
때문에 한번도 그 의미를 파고 들어가 본 적이 없습니다. 그래서 저도 거의 모르는 코드라고 할 수 잇습니다. 패스. |
|
하지만 이제부터는 한 구절이라도 소홀히 해서는 안되는 영역입니다.
Dim ip_address As IPAddress = IPAddress.Parse(ip.Text)
이 코드에서는 제가 폼에 이미 텍스트 박스 하나를 떨어뜨려 놓고 그 이름을 ip 라고 지었다는 전력이
나타나 있습니다. 그래서 그 텍스트박스 안에 타이핑 된 숫자를 파싱한 IPAddress 객체가 바로 ip_address라는
뜻을 위 문장은 간직하고 있습니다.
Dim port As Integer = CInt(po.Text)
이 문장은 이미 명령을 내리는 컴퓨터 코드에서 설명을 한 적이 있습니다. 똑 같으므로 생략합니다. 그리고
이상의 두 코드를 만든 이유는 바로 아래의 코드의 요리 재료를 만들기 위해서였을 뿐입니다.
indi_Listener = New TcpListener(ip_address,
port)
사실 객체지향 언어의 고급 과정은 이런 요리 과정입니다. 이런 식의 요리를 자유자재로 구사할 수 있다면
그 사람은 초고급 엔지니어입니다. 사실 닷넷을 만든 사람도 그렇게 자유롭게 구사하지는 못합니다. 왜냐하면
자신이 만들어 놓은 것이라 하더라도 시간이 흐르면 까먹어버리기 때문입니다.
어쨌든 이상에서 새로운 TcpListener 객체가 하나 만들어졌습니다. 그 객체는 앞으로 본 프로그램을
실행시키면 괄호 속의 IP 주소 정보와 포트번호 정보를 입력 받게 될 것입니다. 그런데 그 아이피 주소와
포트 번호는 어디의 것을 지칭하고 있는 것일까요?
|
|
 |
|
|
그것은 바로 스스로의 - 자기 자신의 - 아이피 주소와 포트 번호입니다. 여자의 경우라면 자신의 성기 입구를
뜻하기 때문입니다. 어떤 남자의 성기가 외서 꽂힐 것인가는 미리 짐작할 수도 없는 것이기에 이는 당연합니다. 그래서
자기 자신의 아이피 주소를 기입하게 되는 것입니다. 물론 포트번호는 남자와 여자 모두 같은 번호를 사용해야 합니다.
그런데 만약 이 경우 아이피 주소를 127.0.0.1로 기입했다면 이 프로그램은 작동을 하게 될까요? 이 질문에
답하기 이전에 먼저 검토해 봐야 할 것이 하나 있습니다.
저희 집은 유동 아이피지만 통상 한달 정도는 아이피가 잘 변하지 않습니다. 지금 확인해 보니 58.233.190.148이
저희 집 wan 아이피 주소입니다. 지금 타이핑을 치고 있는 이 컴퓨터의 사설 아이피 즉 lan 아이피는 192.168.10.103입니다.
그리고 로컬 컴퓨터의 자기 자신을 지칭하는 아이피 주소는 언제나 127.0.0.1(또는 “localhost”)입니다(윈도우
기준. 유닉스나 리눅스는 어떨는지 모르겠네요. 아마 마찬가지가 아닐까 추측됩니다.).
|
|

|
|
만약 192.168.10.103을 집어 넣으면? 아니면 58.233.190.148을
집어 넣으면?
정답은 명령을 보내는 컴퓨터가 어떤 아이피 주소를 사용하고 있는지가 기준이 됩니다. 127.0.0.1을
가지고는 다른 컴퓨터로부터의 수신이 되지 않습니다. 공유기 내부에서의 랜 환경이라면 192.168.10.103을
써야 하며, 공유기 외부의 원격지 컴퓨터(즉 wan 주소가 다른 경우)로부터 접속을 받으려면, 즉 명령을
받으려면 58.233.190.148을 써야 합니다. 그 이유는?
이것이 지금까지도 수차례나 언급했던 “엿장수 마음대로” 원칙 때문입니다. 닷넷의 소켓 개발자 또는 공유기
제조 회사가 프로그램을 그렇게 만들었기 때문입니다.
일반적인 상식대로라면 127.0.0.1을 사용하면 그 자체가 자기 자신을 지칭하므로 자신의 lan 주소인
192.168.10.103과 wan 주소인 58.233.190.148이 입력된 것이나 마찬가지라고 취급될
수 있어야 합니다. |
|
|
그렇게 만드는 것이 원칙입니다. 따라서 만약 내가 127.0.0.1을 사용했다면 그것은 원리적으로 볼 때 잘못된
행동이 아닙니다. 오히려 원리에 충실한 간결한 사고방식입니다.
컴퓨터가 어려운 이유는 바로 이런 이유 때문입니다. 대부분의 프로그램이나 솔루션의 개발자들은 프로급이라 볼 수 없습니다.
조폭들 세계를 흔히 건달과 양아치로 구분하는데 IT쪽의 엔지니어를 그런 식으로 분류한다면 5% 이내의 건달과 95%
이상의 양아치로 구성되어 있는 것이 적어도 한국의 작금의 IT 인력 구조입니다. 이 둘의 구분 잣대는 물론 도덕성입니다.
예를 하나 들자면 시디스페이스라는 - 한국에서는 나름대로 많이 알려졌다고 볼 수 있는 - 프로그램이 있었습니다.
이 프로그램은 윈도우의 레지스트리를 제멋대로 뒤바꾸어 놓습니다. 오로지 자기 프로그램만 돌아갈 수 있는가 여부만이
그들에겐 중요할 뿐입니다. 그런데 이 프로그램이 네로라는 유명한 프로그램과 프로그램 상 충돌을 일으킨 적이 있었습니다.
이 경우 시디스페이스 측에서 게시판에 공시한 내용은 대략 아래와 같았습니다.
“이번의 충돌은 그 원인이 네로측에 있다는 것이 밝혀졌으며 이에 관하여 네로 측과 연락을 취해 이 점을 인정받았다”
사실은 물론 시디스페이스의 무책임한 레지스트리 변경에 문제의 원인이 있었고, 시디스페이스 관계자가 네로측과 연락을
취해 그들로부터 잘못을 인정 받은 적은 더더욱 없었습니다. 이것이 바로 한국 엔지니어들의 일반적인 도덕성입니다. 조직
사회의 양아치 취급 받는 사람들마저도 이런 천박한 짓까지는 하지 않습니다. 각설하고,
|
|
하여간 127.0.0.1로 쓰면 안됩니다. 명령을 내리는 컴퓨터에서 192.168.
10.103을 사용했으면 명령을 받는 컴퓨터에서도 역시 192.168.10.103을 써야 합니다. 앞으로
수십년이 흐른 다음 컴퓨터 프로그래밍이 하나의 체계적인 학문이 되고 나아가 책임있고 자질 있는 사람들로
IT 인력 구조가 재편되게 되면 그 시기에 가서 127.0.0.1을 치게 되면 이제는 프로그램이 돌아가게
될 것입니다. 그때까지는 엿장사 하자는 대로 따라 해 주는 수 밖에 없습니다.
indi_Listener.Start()
가게 내부 공사가 완료되었다면 이제 입구에 간판을 붙여야 할 차례입니다. “어서오십시오”라고 말입니다.
그것이 바로 위의 코드입니다. 여기서 사실상 하나의 스레드가 돌아가기 시작한다고 할 수 있습니다.
이 스레드의 역할은 명령을 내리는 원격 컴퓨터의 연결 요청을 일단 기록해 두는 것입니다. 안마 시술소
같으면 입구 카운터에서 대기 중인 손님 명단을 장부에 일단 임시적으로 기록해 놓는 것입니다. 아직 밀실의
아가씨 측으로부터 OK 사인이 떨어지지 않았기 때문입니다.
|
|
 |
|
|
Dim receive_Thread_sub As New Thread(AddressOf
receive_Thread)
소위 말하는 그 유명한 스레드입니다. 그냥 이런 식으로 스레드를 생성한다고 외워 두셔야 합니다.
이 스레드의 명칭은 receive_Thread_sub입니다. 그리고 그 스레드는 괄호 속에 있는 receive_Thread
라는 별도의 스레드 함수(receive_Thread 는 안마가 실제로 행해지는 방 이름이라고 할 수 있습니다)를 통해서
구현이 됩니다. 그 receive_Thread 함수는 아래 쪽의 코드에서 별도의 함수 형식을 통해서 등장하게 됩니다.
이 함수 내에서 실제로 안마가 행해지게 됩니다. receive_Thread_sub와 receive_Thread 라는
두 개의 명칭을 제외하고는 완전히 정해진 형식입니다. 당근 무조건 외워야 합니다.
receive_Thread_sub.Start()
스레드를 시작한다는 선언입니다. 이로써 밀실로 입장이 이루어집니다.
End Sub
명령을 받는 컴퓨터에서의 버튼 이벤트가 끝나는 부분을 표시하고 있습니다. 하지만 스레드가 가동되고 있기 때문에 프로그램
자체가 끝나게 되는 것은 아닙니다.
안마는 다음 회에 해 드리겠습니다.
|
|