중독적 습관

요즘 말하는 욜로(YOLO) 에 가까운 삶을 사는 친구가 있다. 일단 불안한 계약직인데다 계약 텀도 굉장히 짧은 직종에 근무한다. 그런데도 잘 놀러 다닌다. 너무나도. 그러면서 늘 걱정을 늘어놓는다. 좋은 남자 만나 결혼해야지, 정규직도 되면 좋겠지, 계속 놀러 다니고는 싶지… 이런, 쓰고 보니 이 친구는 욜로가 아니다. 내일 살 걱정을 하기는 하니까. 아무튼, 이 친구의 문제는 뭘까, 혹시 쾌락의 끝자락에서 내려오기 싫은 발버둥을 치는 것은 아닐까, 아니면 더 큰 것을 좆을 수 없는 공허함을 느낀 걸까.

술을 마시던 게임을 하던, 그 때만 즐거을 뿐이다. 숙취에 고통받을 때, 게임 종료 버튼 앞에 있을 때, 우리는 다시 비어있음을 느낀다. 쾌락의 순간에는 더 큰 역치를 넘어서지 못하는 한계에 부딪히며 파열을 발생시키는 게 아닐까. 그것이 허무감인지, 어떤건지 잘 모르겠다.

더 큰 보상을 바라는 뇌의 요구와, 중독적인 일 외에 할 수 있는 일을 하나씩 배제시키는 습관의 힘이 합쳐진 것을 우리는 중독이라고 한다. 올바른 (적어도 사회적으로 올바르다고 여겨지는) ‘성취’의 대부분은, 즉각적인 보상으로 이어지지 않는다. 그래서 상벌이 바로 튀어나오는 행위에 우리는 열광한다. 시간을 들여 보상이 주어지는 성취에 점점 관심을 잃는 것이다. 하는 방법도, 버틸 힘도 잃어버린다.

습관은 더 무서운데, 뇌의 보상기제가 작동하건 말건 이 녀석은 항상 LRU (Least Recently Used) 리스트처럼 행위를 관리한다는 것이다. 자주 한 일일수록 쉽게 선택되고, 전혀 해보지 않은 일 (하지만 한번 쯤 해봐야지 하며 버킷리스트에 채워넣었던 일) 은 선택되기 힘들게 한다. 즉, 새로운 도전은 그만큼의 비용이 들지만, 곁에 둔 습관은 그렇지 않다는 것이다. 반복적인 행동은, 그것이 나쁘건 좋건 간에, 여유 시간에 ‘그냥 할 만한 것들’의 유력한 후보로 항상 존재할 것이다. 그 후보는 2선, 3선을 밥먹듯이 할테고, 질 나쁜 행동이 고착화되면 언젠가는 부패할 것이다.

잘못된 습관과 보상기제로 인해 정해진 중독적 행위가 과연 현재를 즐기자라는 다소 낙관적인 말로만 포장될 수 있는지에 대해 숙고해 봐야 한다는 것이다. (잘못됐다는 말을 쓰는걸 별로 좋아하지 않지만, 뭔가 내가 하는 일이 이상하다고 생각된다면 그건 잘못된 거다. 애석하게도. )

소미의 옆집 아저씨도 ‘오늘만 산다’고 말하지만, 욜로라고 하지 않는다. 다음을 걱정을 하는 자에게, 중독을 이어가는 이 허무함은 더 고통스럽지 않을까? 그래서 이 신조어는, 실제로 그런 극단적인 허무감을 회피하지 못한 비웃음의 단어는 아닌지, 아니면 정말 해탈했다고 자랑하고 싶은건지 의심스럽기만 하다.

암보스 (ambos)

두 여자가 서로 다른 방향을 보고 있지만, 그 얼굴 안에서는 마주보기도 하는 듯, 흑심을 품고 있는 듯한 이미지가 프랙탈처럼 나타나 있는 기괴한 표지에 담긴 내용은 대체 어떤 사연일까?

출판사 ‘황금가지’에서 만든 새로운 라인업 ‘수상한 서재’ 시리즈의 첫 작품인 김수안 작가의 암보스를 읽어봤다. 시간이 없어 서울-대구를 오가는 KTX 안에서 읽었는데, 다소 두꺼운 외형과는 달리 꽤나 빨리 따라갈 수 있었다.

암보스 (ambos) 는 스페인어로 ‘양쪽, 두 사람’ 이란 뜻이다. 두 여자가 주인공인 것을 표현하고자 했겠으나, 실제로 스페인어는 남성형/여성형 명사가 따로 존재한다. 그래서 표지만 보고는 왜 암바스 (ambas) 라는 여성형 명사를 채택하지 않았을까 자뭇 궁금해졌다. arm boss 같은 느낌도 있어서 나중에 작가 인터뷰를 보니, 두 주인공만을 위한 제목이 아닌 여러 인물들의 관계 자체를 암보스로 봤기 때문이라고 한다.

줄거리

이하는 최대한 스포일러를 피해서 나온 (그래서 책 소개내용과 거의 흡사한) 초반 상황 요약이다.

신문 기자 이한나 는 어느 날 방화사건 현장에 있었고, 목격한 모든 정보를 회사에 전달한 뒤 의식을 잃었다. 이대로 죽는건가… 아니, 죽는 것도 나쁘지 않겠지. 무능하고 철면피인 아버지가 진 빚이며, 헤어진 남자친구며, 내가 잘못 굴린 펜으로 사람이 곤란에 겪었으니까.

깨어나보니, 이한나는 다른 사람이 되어 있었다. 같은 날 옥상에서 뛰어내렸지만 가까스로 목숨을 건졌던 강유진이란 사람으로. 이한나는 퇴원하자 마자 강유진의 집을 찾아갔는데, 별안간 이한나의 모습을 한 누군가가 뒤따라 찾아왔다. 그는 강유진 이었다.

몸이 뒤바뀐 것이다. 어떻게 된 일일까.

초자연은 중요한 게 아니다

하지만 책은 왜 이들의 몸이 바뀌었는지는 독자들에게 이야기해주지 않는다. 누군가가 상대방의 몸을 원했다면, 영화 ‘더 게임’ 의 강회장 (변희봉 扮) 같은 캐릭터가 나와야 하겠지만, 여기선 어느 누구도 그런 역을 자처하지 않는다. 그보다는, 서로의 삶에서 느끼는 ‘잃은 것과 얻은 것의 의미’를 알아가고 행동하는 데에 많은 부분을 할애한다. 강유진은 비만에 집에 틀어박혀 지내기 일쑤지만 돈이 많았고, 이한나는 예쁜데다 활기차고 자기주관이 강했지만 안하무인 아버지로 인해 많은 빚을 졌다.

설마 강유진의 모습을 한 이한나가 ‘나는 열심히 운동해서 살을 빼야지’ 라거나 ‘이제부터 사람들과 잘 어울려야지’ 같은 뭐 이런 희망적인 스토리를 기대하지는 말자. 그들은 언젠가 다시 본래의 상태로 돌아갈 것이라고 확신하고 있었다. 정확히 말하자면 강유진이 그렇게 될 것이라고 이야기했지만. 아무튼 그렇게 돌아가버린다고 가정했을 때 이들은 어떤 행동을 취할까. 상대방의 미스터리한 행적이 서로의 시선을 통해 서술되는 그 순간.

갑자기 교차하는 사건

연쇄살인사건, 그리고 그 범인을 찾는 형사가 교차되어 나타난다. 일면 관련없어 보이는 사건 이야기가 갑자기 주인공 일행과 교차점을 지나면서 충돌하기 시작한다. 그것도 아주 빠르게. 파열음은 의외로 강하고, 당사자들의 추리 게임은 꼬리에 꼬리를 문다.

형사 측 인물에서 가장 눈에 띄는 것은 단연 박선호 형사일 것이다. 우락부락한 체격과 어울리지 않게 집요하리만치 파고드는 집중력이 소설 내내 돋보인다. 그 옆의 부사수 칠범 역시 파트너로서의 역할에 충실한다. 이한나의 가족과 주변인, 강유진의 증언 등으로 그 사건 이후 사람이 달라졌다는 부분을 끝까지 물고 늘어지는데, 이 부분에서 주인공 일행과의 긴박한 밀당이 계속 이뤄진다. 결국 살인사건은 실마리를 찾고 해결되지만… 이게 정말 끝일까?

이야기 하는 방식

소설이 가지는 강점은 심리 묘사와 비유에 많은 에너지를 쏟았고 그걸 고스란히 전달하려고 노력했단 점이다. 사건의 진위가 아니라, 사건에 휘말린 인물의 세세한 면면을 나타내려고 애를 썼다. 그래서 스토리 자체에 태클을 걸면서 본다면 자칫 넘어지기 쉬울 것 같아 보이긴 하지만, 그런 세세한 부분을 너그러이 이해해준다면 재밌게 읽힐 소설이지 않을까.

작중 이한나의 시점, 박선호 (를 포함한 외부)의 시점에서는 이한나와 강유진을 지칭하는 자아가 서로 다른 것이 신선했다. 이한나의 시점에서 서술될 때는 ‘나’ 와 ‘(내 모습을 한) 강유진’ 이지만, 그 외에는 외모대로 ‘강유진’ 과 ‘이한나’ 로 서술된다. 박선호가 이를 눈치챈 종반부에서는 서술이 다시 뒤바뀌긴 하지만. 그래서 이 부분을 따라가기가 조금 피곤해 질 수는 있겠다.

마치며

독자에게 추리할 여지를 많이 주는 것 같지만, 사실 복선은 야속하게 정류장을 지나치는 시내버스 같이 지나간다. 어느샌가 소설 속 인물들의 추리보다 한발 앞서 나간게 아닐까, 그랬던 거였어! 라고 생각하고 있다면, 조심해야 한다. 끈적한 손으로 뒤통수를 후려갈겨서 뒷맛이 찜찜하다. 이게 뭐야, 꼭 그렇게 했어야만 했냐! 같은 느낌. 하지만, 역으로 생각해보면 책을 한번 더 돌려보게 만드는 매력을 지니고는 있다고 볼 수 있다.

소설 중에 이런 내용이 있다. 강유진의 모습을 한 이한나가 창문을 바라본다. 창문에는 강유진이 보인다. 내가 정말 나인지, 상대방이 내 모습을 하고 유리창에 나타난건지, 정말로 상대방이 내가 된건지. 나는 누구일까.

사람의 몸이 뒤바뀐다는 초자연적인 전개에만 관심을 가지면 이 소설은 거기서 끝난다. 상대방의 거죽을 쓰고 자신도 몰랐던 민낯이 드러나는 것에 화들짝 놀랄 수 있길 바란다.

Atomic Operation 으로 하는 동시성 제어

Test-And-Set (TAS)

TAS 를 이용해서 간단한 동시성 제어를 할 수 있다. testAndSet 이라는 function 을 가지고 아래의 do...while 문을 쓰레드 A, B 에서 동시에 호출한다고 해 보자. 이 때 lock 은 같은 변수이다.

function TestAndSet(boolean_ref lock) {
    boolean initial = lock
    lock = true
    return initial
}

do {
    while(TestAndSet(&lock))
        ; // do nothing
    // critical section
    lock = false;
    // remainder section
} while(true);

우선 TestAndSet() 은 다음과 같은 일을 한다. 말 그대로 ‘지금 값이 무엇인지 검사하고, 값을 바꾼다’ 는 것이다.

  1. lock의 현재 값을 저장해 둔다.
  2. lock의 값을 true 로 설정한다.
  3. 저장한 lock 의 값을 반환한다.

그럼 이걸로 어떻게 아래 block 의 critical section 에 대한 동시성 제어를 할 수 있을까? Thread A 가 먼저 실행했다고 가정하면, 이런 시나리오가 된다.

  1. A : TestAndSet() 의 반환값이 false 이다. while 문을 빠져나온다.
  2. B : TestAndSet() 의 반환값이 true 이다. (A가 true로 두고 나왔기 때문에) while 문에서 계속 돈다.
  3. A : Critical Section 수행 후, lock 을 false 로 바꾼다.
  4. B : 여러 번의 TestAndSet() 호출 후에, 드디어 반환값이 false 가 되었다
    (A가 false 로 두고 나왔기 때문에) while 문을 빠져나온다.

자, 그런데 뭔가 이상하다. 이렇게 이상적으로 동작하지 않을 것 같다. TestAndSet() 함수를 라인별로 동시에 실행한다고 하면 이런 사단이 날 수 있다.

  1. A : TestAndSet() 에 진입해 lock 값을 저장한다. 이 값은 false 이다.
  2. B : TestAndSet() 에 진입해 lock 값을 저장한다. 이 값은 false 이다.
  3. A : TestAndSet() 에서 lock 값을 true 로 바꾼다.
  4. B : TestAndSet() 에서 lock 값을 true 로 바꾼다.
  5. A : TestAndSet() 에서 저장한 값을 반환한다. 이 값은 false 이다.
  6. B : TestAndSet() 에서 저장한 값을 반환한다. 이 값은 false 이다.
  7. A & B : 모두 동시에 critical section 을 수행한다.

그럼 어떡하나? TestAndSet() 은 그래서 저런 함수만으로는 안 되고 Test-And-Set 의 연산이 일관되도록 조정해야 한다. 함수 안에 spinlock 을 쓰면 되겠네요? 싶겠지만 lock 구현하자고 lock 을 또 만드는 건 아닌 것 같다. 그래서 Test-And-Set 은 CPU에서 지원하는 Atomic Instruction 을 사용한다.

Fetch-And-Add : Ticket Lock

Atomic Operation 으로 구현할 수 있는 Lock 중에 Ticket Lock 이 있는데, Fetch-And-Add 로 구현할 수 있는 방법을 알아보자.

ticketLock_init(int *next_ticket, int *now_serving)
{
    *now_serving = *next_ticket = 0;
}

ticketLock_acquire(int *next_ticket, int *now_serving)
{
    my_ticket = fetch_and_inc(next_ticket);
    while(*now_serving != my_ticket) {}
}

ticketLock_release(int *now_serving)
{
    now_serving++;
}

TAS 의 케이스를 이해하고 본다면 별 다른 설명이 필요 없을 것 같다.

  1. Table Lock 초기화를 한다.
  2. A : Table Lock 을 얻으려 한다. 이미 얻었던 my_ticket (0) 과 now_serving (0) 이 같은 값이므로 곧바로 빠져나온다.
  3. B : Table Lock 을 얻으려 한다. 이미 얻었던 my_ticket (1) 과 now_serving (0) 이 다른 값이므로 while 문에서 대기한다.
  4. C : Table Lock 을 얻으려 한다. 이미 얻었던 my_ticket (2) 과 now_serving (0) 이 다른 값이므로 while 문에서 대기한다.
  5. A : Table Lock 을 해제한다. now_serving (0) 을 증가시켜 now_serving (1) 을 만든다.
  6. B : 비로소 Table Lock 을 얻었다. (C는 여전히 대기 중이다.)

여기서 핵심은 fetch_and_inc 인데, 마찬가지로 얻어오는 루틴과 값을 증가시키는 루틴이 따로 떨어져 있으면 중복된 티켓을 들고 기다리는 쓰레드들이 발생할 수 있다. 따라서 이것도 atomic operation 이 되어야 한다.