내 밥벌이가 Python 언어로 개발하는 일이 아니다보니, Python으로 뭔가 만드는 일이 1년에 한 번 있을까 말까한다. (누가 시켜서 만든다기 보다는 일하다가 자동화가 필요한 상황이 생길 때 ~~~)
오늘도 Python Logging 기능을 5년 만에 쓸 일이 있어서 다시 작성하려고 보니, 구글링해야 하고.. 예제 코드 작성해서 확인하고, 그런 후에 작성하려고 한 application에 적용하고... 이렇게 시간을 보내고 나니까 나 스스로 참 답답한 느낌이다. 다음 5년 뒤에도 이런 답답함이 없기를 바라면서 오늘 작성한 것을 잘 메모해야 겠다 ^^
#!/usr/bin/python3
import logging
import logging.handlers
import os
import datetime
import threading
import time
class MyLog():
def __init__(self, dir, logname) -> None:
self.logname = logname
self.dir = os.path.join(dir,logname)
self.InitLogger()
def InitLogger(self):
## Log format 설정하기
formatter = logging.Formatter("[%(asctime)s] %(levelname)s %(filename)s:%(lineno)d - %(message)s")
if os.path.exists(self.dir) == False :
os.makedirs(self.dir)
log_File = os.path.join(self.dir, self.logname + ".log")
timedfilehandler = logging.handlers.TimedRotatingFileHandler(filename=log_File, when='midnight', interval=1, encoding='utf-8')
timedfilehandler.setFormatter(formatter)
timedfilehandler.suffix = "%Y%m%d.log"
self.logger = logging.getLogger(self.logname)
self.logger.addHandler(timedfilehandler)
self.logger.setLevel(logging.INFO)
self.delete_old_log(self.dir, 3)
now = datetime.datetime.now()
self.toDay = "%04d-%02d-%02d" % (now.year, now.month, now.day)
self.th_auto_delete = threading.Thread(target=self.auto_delete, daemon=True)
self.th_auto_delete.start()
'''
함수 인자 설명:
- fpath:삭제할 파일이 있는 디렉토리,
- age:경과일수
'''
def delete_old_log(self, fpath, age):
for fp in os.listdir(fpath):
fp = os.path.join(fpath, fp)
if os.path.isfile(fp):
timestamp_now = datetime.datetime.now().timestamp()
if os.stat(fp).st_mtime < timestamp_now - (age * 24 * 60 * 60):
try:
os.remove(fp)
except OSError:
print(fp, 'this log file is not deleted')
def auto_delete(self):
while True:
now = datetime.datetime.now()
day = "%04d-%02d-%02d" % (now.year, now.month, now.day)
if self.toDay != day:
self.toDay = day
self.delete_old_log(self.dir, 3)
time.sleep(600)
## This is test code.
if __name__ == '__main__':
log = MyLog("my_log_test_dir", logname="sejong")
for idx in range(3):
log.logger.info(f"로그 메시지가 잘 기록되는지 테스트 중 {idx}")
테스트는 아래와 같이 하면 된다. 일단, 잘 동작함, OK !!
$ ./my_log.py
...
위 'MyLog' 클래스를 다른 Application code에서 참고한다면, 아래 예제와 같이 작성하면 된다.
참고: my_log.py는 아래 소스 코드 my_sample_app.py와 같은 디렉토리에 있어야 한다.
#!/usr/bin/python3
import my_log
...
... 중간 생략 ...
...
if __name__ == '__main__':
log = my_log.MyLog("log_my_sample_dir", logname="my_sample")
log.logger.info("##### Start program #####")
... 중간 생략 ...
Kubernetes service resource를 삭제하고 다시 생성하면, Public IP(또는 EXTERNAL-IP) 값이 변경된다.
AWS, Oracle Cloud, Azure, GCP(Google Cloud Platform) 같은 Public Cloud Infra에서 제공하는
Reserved Public IP 기능과 Service YAML에 'externalIPs' 값을 설정하면 Public IP Address를 고정시킬 수 있지만,
Helm Chart를 작성하거나 Kubernetes Service Manifest 파일을 작성할 때마다
Reserved Public IP 생성 요청
위에서 생성된 Public IP 값을 확인 후 Service Manifest 작성(또는 Helm chart) 작성
한다는 것은 참 불편한 작업이다.
그래서 아래의 기능을 수행하는 Python script를 만들었다.
my.yaml 파일에서 Public IP address 정보를 갱신할 'Kubernetes service' 정보와 'Internet domain name' 목록을 설정
DNS 서버에서 직접 Kubernetes API server에게 매번 변경되는 Service resource의 public IP address를 조회 요청
이렇게 얻은 public IP address(즉, EXTERNAL-IP)를 자동으로 DNS 서버의 Zone file에 반영
BIND9 서비스 데몬을 재기동
#!/usr/bin/python3
##############################################################
##
## Written by "Andrew Jeong"
## Date: 2024.03.25
## File: kube-dns-auto-config.py
##############################################################
import os
import time
import socket
import shutil
import subprocess
import yaml
from datetime import datetime
from kubernetes import client, config
##
## File 내용 중에서 일부 문자열을 찾아서 바꿈.
##
def replace_in_file(file_path, old_str, new_str):
# 파일 읽어들이기
fr = open(file_path, 'r')
lines = fr.readlines()
fr.close()
# old_str -> new_str 치환
fw = open(file_path, 'w')
for line in lines:
fw.write(line.replace(old_str, new_str))
fw.close()
##
## Zone file에 있는 Serial 값을 찾기
## NOTE: 주의할 점
## Zone file 안에 Serial 값이 아래 포맷으로 저장되어 있어야 한다.
##
## @ IN SOA mydomain.kr root.mydomain.kr (
## 2024032502 ; Serial
## 3600 ; Refresh
## 900 ; Update retry
## 604800 ; Expiry
## 600 ; TTL for cache name server (for 30 minutes)
## )
##
def search_old_serial(file_path):
fp = open(file_path)
lines = fp.readlines()
fp.close()
for line in lines:
# print(f"line: {line}")
if line.find('Serial') > 0:
# print(f"I found serial value: {line}")
words = line.split(';')
old_serial = words[0].strip()
# print(f"old_serial: [{old_serial}]")
return old_serial
##
## Update 'serial number' of zone file
##
def update_serial_for_zone_file(file_path):
old_serial = search_old_serial(zone_file)
curr_date = datetime.today().strftime("%Y%m%d")
bool_today = old_serial.startswith(curr_date)
if bool_today is True:
## Serial 값이 오늘 한번이라도 Update된 경우
int_old_serial = int(old_serial)
new_serial = str(int_old_serial + 1)
else:
## Serial 값이 오늘 처음으로 Update되는 경우
new_serial = f"{curr_date}00"
print(f"old_serial: [{old_serial}]")
print(f"new_serial: [{new_serial}]")
## copy file for backup
bkup_file = f"{zone_file}_{new_serial}"
shutil.copy(zone_file, bkup_file)
## Update serial number
replace_in_file(zone_file, old_serial, new_serial)
def update_dns(conf):
kube_svc_name = conf['kube_svc_name']
kube_domain_name = conf['kube_domain_name']
print(f"\n[ Checking configuration '{kube_svc_name}' / '{kube_domain_name}' ]")
for item in svc_info.items:
if item.metadata.name == kube_svc_name and item.metadata.namespace:
ip_k8s_svc = item.status.load_balancer.ingress[0].ip
print(f" Kubernetes Service External-IP: {ip_k8s_svc}")
ip_addr_domain_name = socket.gethostbyname(kube_domain_name)
print(f" {kube_domain_name}: {ip_addr_domain_name}")
if ip_addr_domain_name == ip_k8s_svc:
print(" IP address of domain name is not changed. Nothing to do.")
return -1
else:
update_serial_for_zone_file(zone_file)
print(" IP address of domain name is changed.")
print(" Make a backup file of zone file and then")
print(f" update '{kube_domain_name} IN A {ip_addr_domain_name}' record")
print(f" {ip_addr_domain_name} -> {ip_k8s_svc}")
replace_in_file(zone_file, ip_addr_domain_name, ip_k8s_svc)
return 0
## ------------------------------------------------------------------------
## Begin of Main
## ------------------------------------------------------------------------
## Load configration from YAML file.
with open('/root/WorkSpace/kube-python-client/my.yaml') as fp:
my_conf_info = yaml.load(fp, Loader = yaml.FullLoader)
zone_file = my_conf_info['zone_file']
##
## Get new IP address from Kubernetes service resource
## and
## Get old IP address from DNS
##
## Configs can be set in Configuration class directly or using helper utility
config.load_kube_config()
v1 = client.CoreV1Api()
while True:
svc_restart_flag = 0
svc_info = v1.list_service_for_all_namespaces(watch=False)
for conf in my_conf_info['services']:
ret = update_dns(conf)
if ret == 0:
svc_restart_flag = 1
if svc_restart_flag == 1:
ret_value = subprocess.call("systemctl restart bind9", shell=True)
print(f"\n\n[ BIND9 is restarted ] Return: {ret_value}\n\n")
time.sleep(300)
## ------------------------------------------------------------------------
## End of Main
## ------------------------------------------------------------------------
지금 급하게 DNS 서버(Bind9)의 Serial 값을 자동 갱신하도록하는 기능이 필요해서 작성해본 Python script이다.
동작 방식을 간단하게 설명하면 이렇다.
만약 오늘이 "2024년 03월 25일"이라고 가정하면,
기존 Zone 파일의 Serial 값이 2024032501 이면, 2024032502 으로 끝자리면 1 증분시켜줌.
기존 Zone 파일의 Serial 값이 2024032401 이면, 2024032500 으로 날짜를 오늘로 변경하고 끝 2자리는 00으로 설정
아래 Script를 복사해서 바로 실행하면 잘 동작할 것이다.
$ cat update_zone_serial.py
#!/usr/bin/python3
from datetime import datetime
import shutil
##
## File 내용 중에서 일부 문자열을 찾아서 바꿈.
##
def replace_in_file(file_path, old_str, new_str):
# 파일 읽어들이기
fr = open(file_path, 'r')
lines = fr.readlines()
fr.close()
# old_str -> new_str 치환
fw = open(file_path, 'w')
for line in lines:
fw.write(line.replace(old_str, new_str))
fw.close()
##
## Zone file에 있는 Serial 값을 찾기
## NOTE: 주의할 점
## Zone file 안에 Serial 값이 아래 포맷으로 저장되어 있어야 한다.
##
## @ IN SOA mydomain.kr root.mydomain.kr (
## 2024032502 ; Serial
## 3600 ; Refresh
## 900 ; Update retry
## 604800 ; Expiry
## 600 ; TTL for cache name server (for 30 minutes)
## )
##
def search_old_serial(file_path):
fp = open(file_path)
lines = fp.readlines()
fp.close()
for line in lines:
if line.find('Serial') > 0:
words = line.split(';')
old_serial = words[0].strip()
return old_serial
zone_file = '/var/cache/bind/mydomain.kr.zone'
old_serial = search_old_serial(zone_file)
curr_date = datetime.today().strftime("%Y%m%d")
bool_today = old_serial.startswith(curr_date)
if bool_today is True:
## Serial 값이 오늘 Update된 경우
int_old_serial = int(old_serial)
new_serial = str(int_old_serial + 1)
else:
## Serial 값이 며칠 전에 Update된 경우
new_serial = f"{curr_date}00"
## copy file for backup
bkup_file = f"{zone_file}_{new_serial}"
shutil.copy(zone_file, bkup_file)
replace_in_file(zone_file, old_serial, new_serial)
## 실행 테스트
$ ./update_zone_serial.py
Bind9 Zone 파일의 Serial 값을 변경하는 Bash Script
만약, Python 인터프리터가 없는 경우라면 아래와 같이 bash 스크립트를 작성해서 실행해도 동일한 결과를 얻을 수 있다.
$ cat my-dns-zone-serial-increase.sh
#!/usr/bin/env bash
set -euo pipefail
: ${1?"Usage: $0 <zone file>"}
IFILE=$1
if [ ! -w "${IFILE}" ]; then
echo "Error cannot write to ${IFILE}"
exit
fi
if [ ! -w $(pwd) ]; then
echo "Error, sed needs write permission for temp file, add w to current directory"
exit
fi
PREV_SERIAL=$(grep -i Serial "${IFILE}" | awk '{print $1}')
echo "PREV_SERIAL: ${PREV_SERIAL}"
TODAY=$(date +%Y%m%d00)
if [ "$PREV_SERIAL" -ge "${TODAY}" ]; then
NEW_SERIAL=$((PREV_SERIAL+1))
else
NEW_SERIAL=${TODAY}
fi
echo "NEW_SERIAL: ${NEW_SERIAL}"
sed -i "s/${PREV_SERIAL}/${NEW_SERIAL}/" "${IFILE}"
printf "Zone: %s [%d -> %d]\n" "${IFILE}" "${PREV_SERIAL}" "${NEW_SERIAL}"
## 실행 테스트
$ ./my-dns-zone-serial-increase.sh /var/cache/bind/mydomain.kr.zone
그 동안 키보드를 살 때는 애플 제품을 리뷰해주는 유명 유튜버의 키보드 리뷰를 보고 키보드를 구입했다.
맥북, 맥미니가 대중적이지 않을 때는 맥북과 키배열이 호환되는 제품을 찾기가 어려워서 유튜버의 리뷰를 보고 구입했었다.
(그래봐야 결국 구입하는 것은 KeyChron 계열의 키보드 ㅠㅠ)
이제는 대부분의 키보드가 "Windows/Mac" 전환 스위치를 지원하니까 유튜브 리뷰를 보면서 macOS 호환 키보드를 찾을 필요가 없어졌다.
그냥 키보드를 타건해보고 마음에 드는 키감을 찾아서 구입하면 된다.
오늘 큰 마음을 먹고, 멀고 먼 용산전자상가에 있는 타건샵 '구산컴넷'을 갔다.
(운전하면서 가다가 에너지 다 썼다 ㅠㅠ 멀어도 너무 멀어 ㅠㅠ)
구산컴넷 위치 (선인상가 21동 2층)
'용산전자상가 제1공영주차장'에 주차를 하고, 선인상가 21동으로 입장.
토요일이라서 대부분 매장이 문을 닫아서 빌딩 내부가 어둡다.
선인상가 21동 2층으로 올라가니, 구산컴넷 매장(153호)에 사람이 많았다.
그냥 사람이 많은 정도를 넘어서 매장 내부로 입장을 못할 정도로 많았다.
와! 이렇게 사람이 많을 수 있나?
2~5만원 대의 키보드가 진열된 부분에는 기다리지 않고 키보드를 타건해볼 수 있었다.
그러나 12~19만원 정도의 제품이 진열된 쪽은 한참 기다려야 키보드 1개를 타건해볼 수 있었다.
그렇게 100개 정도 되는 키보드를 하나하나 쳐보고, 내 마음에 드는 제품을 3개로 좁혔다.
그리고 3개 키보드를 왔다갔다 타건해보면서 나에게 맞는 키보드를 찾아봤다.
선택하기 어려웠다.
자리를 옮겨서 왔다갔다 할때마다 내 앞에 다른 사람이 타건하는 것이 끝날 때까지 기다려야 해서
그 시간에 이전 타건했던 제품의 느낌이 기억에서 사라졌다 ㅠㅠ
내가 고른 3개의 기계식 키보드는 공통점이 있다.
- 키압이 35g~38g (즉, 살살~ 물 흐르듯 타이핑하는 나에게는 이런 키압이 딱 좋다)
- "따각따각~" "또각또각~" 하는 음이 아닌 "드드드~" 이런 느낌이 나는 키보드
결국 마지막에 선택한 제품은 '한성 TFX GTO Wireless 38g 백축'을 골랐다.
색상은 Santorini. 아주 시원하고 산뜻한 색상이다. ㅎㅎ
100개의 키보드를 직접 내 손으로 쳐보고 하나하나 키를 누르는 느낌을 비교하면서 구입하니까
만족도가 아주 높다.
인터넷으로 구입하는 가격이나 구산컴넷에서 구입하는 가격이나 똑같다. 그러니까 직접 가서 타건을 해보고 나에게 딱 맞는 키보드를 구입하는 것을 추천 !!! 그리고 배송을 기다리지 않고 바로 제품을 구할 수 있다는 것도 큰 장점 ^^
아래 사진은 오늘 매장에서 둘러보면서 찍은 것.
만약 15~20만원 정도 소비할 생각으로 왔다면, 아래 사진에서 "빨간색 별표"로 표시한 진열대로 직행하는 것을 추천한다. (시간 낭비를 안 하기 위해서)
구산컴넷 매장 내부
다른 진열대는 2~9만원대 키보드를 진열했는데, 아마 기계식 키보드를 수년간 써온 사람이라면 만족할만한 키감이 없을 것이다.
그냥 바로 "빨간색 별표" 표시한 진열대로 쭉 직행하는게 좋다.
그리고 저기에 사람들이 대부분 몰려있다.
싼 키보드는 매장의 외곽에 배치하고, 비싼 키보드는 매장의 안쪽에 배치한 사장님의 치밀한 판매 전략을 느낄 수 있었다 ^^
구산컴넷 매장 (복도에서 바라본 매장 모습)
예쁜 마우스, 독특한 마우스도 많았다.
다양한 마우스
구산컴넷에 도착해서 키보드 타건해보고, 구입하기까지 대략 30분 정도 시간을 쓴 것 같다.
토요일 점심 때라서 사람이 많아서 제품마다 타건할 때마다 10~20초 정도씩 기다려야 했던 탓에 시간을 많이 썼다.
아마 평일 한가한 시간에 갔거나 토요일 아침 일찍 간다면 10분 정도 전체 제품을 다 타건해보고 바로 제품을 구입하지 않았을까 생각해본다.
##
## 채용 관련 글
##
제가 일하고 있는 기업 부설연구소에서 저와 같이 연구/개발할 동료를 찾고 있습니다.
(이곳은 개인 블로그라서 기업 이름은 기재하지 않겠습니다. E-mail로 문의주시면 자세한 정보를 공유하겠습니다.)
근무지 위치:
서울시 서초구 서초동, 3호선 남부터미널역 근처 (전철역 출구에서 회사 입구까지 도보로 328m)
필요한 지식 (아래 내용 중에서 70% 정도를 미리 알고 있다면 빠르게 협업할 수 있음):
- 운영체제 (학부 3~4학년 때, 컴퓨터공학 운영체제 과목에서 배운 지식 수준):
예를 들어, Processor, Process 생성(Fork)/종료, Memory, 동시성, 병렬처리, OS kernel driver
- Linux OS에서 IPC 구현이 가능
예를 들어, MSGQ, SHM, Named PIPE 등 활용하여 Process간 Comm.하는 기능 구현이 가능하면 됨.
- Algorithm(C언어, C++ 언어로 구현 가능해야 함)
예를 들어, Hashtable, B-Tree, Qsort 정도를 C 또는 C++로 구현할 수 있을 정도
- Network 패킷 처리 지식(Layer 2 ~ 4, Layer 7)
예를 들어, DHCP Server/Client의 주요 Feature를 구현할 정도의 능력이 있으면 됨.
- Netfilter, eBPF 등 (IP packet hooking, ethernet packet 처리, UDP/TCP packet 처리)
- IETF RFC 문서를 잘 읽고 이해하는 능력 ^^
# 위에 열거한 내용 외에도 제가 여기 블로그에 적은 내용들이 대부분 업무하면서 관련이 있는 주제를 기록한 것이라서
# 이 블로그에 있는 내용들을 잘 알고 있다면, 저희 연구소에 와서 연구/개발 업무를 수행함에 있어서 어려움이 없을 겁니다.
회사에서 사용하는 프로그래밍 언어:
- 프로그래밍 언어: C, C++, Go
(참고: 아직 연구소 동료들이 Rust를 사용하진 않습니다만, 새 언어로써 Rust를 사용하는 것을 고려하는 중)
근무 시간:
- 출근: 8~10시 사이에서 자유롭게 선택
- 퇴근: 8시간 근무 후 퇴근 (오후 5시 ~ 7시 사이)
- 야근 여부: 거의 없음 (내 경우, 올해 상반기 6개월간 7시 이후에 퇴근한 경우가 2회 있었음)
- 회식 여부: 자유 (1년에 2회 정도 회식하는데, 본인이 집에 가고 싶으면 회식에 안 감. 왜 참석 안 하는지 묻지도 않음)
외근 여부:
- 신규 프로젝트 멤버 -> 외근 전혀 하지 않음 (나는 신규 프로젝트만 참여해서 지난 1년 동안 한번도 외근 없었음)
- 상용 프로젝트 멤버 -> 1년에 5회 미만 정도로 외근
팀 워크샵 여부:
- 팀 워크샵 자체를 진행하지 않음. (워크샵 참석하는 거 싫어하는 개발자 환영 ^^)
연락처:
- "sejong.jeonjo@gmail.com" # 궁금한 점은 이 연락처로 문의주세요.
- 블로그 비밀 댓글 (제가 하루에 한번씩 댓글 확인하고 있음)
원하는 인재상:
- 우리 부설연구소는 "긴 호흡으로 프로젝트를 진행"하기 때문에 최소 2년간 한 가지 주제를 꾸준하게 연구/개발할 수 있는 개발자를 원함.
- 우리 부설연구소는 자주적으로 연구 주제를 찾아서 업무를 하기 때문에 능동적으로 생각하고 행동하는 동료를 원함.
- 차분하게 연구 주제에 몰입하고, 해법을 찾는 것을 즐기는 사람.
내가 느끼는 우리 연구소의 장점:
- 갑/을 관계가 없음. (제가 근무하고 있는 연구소는 SI업종이 아니라서 갑/을 회사 개념이 없음)
- 연구소 자체적으로 연구 주제를 발굴하고 시스템을 개발하기 때문에 개발 일정에 대한 스트레스가 적음
- 빌딩 전체를 우리 회사가 사용하므로 분위기가 산만하지 않음.
- 근처에 예술의전당, 우면산 둘레길이 있어서 점심 시간에 산책하기 좋음 ^^
- 연구소 동료들 매너가 Good (2년간 일하면서 한번도 감정에 스크레치 생기거나 얼굴 붉히며 싸운 적 없음 ^^)
처음 회사에서 일할 때는 10년 정도 프로그래밍하고, 소프트웨어 설계하는 경험이 누적되다보면 자연스럽게 상당한 지식이 생길 것 같았다.
한 회사, 한 부서에서만 일했을 때는 이런 생각이 어느 정도 맞았다.
3년 정도 한 부서에서 IT 시스템 개발하고, 배포, 운영까지 해보니까 내가 제일 많이 알고, 깊게 알고, 제일 능력자처럼 보였다.
내 인생 두번째 회사에 입사
그런데 4년차에 회사를 한번 옮겨보니, 모든 것이 초기화되었다.
내가 제일 능력자인 줄 알았는데, 새로운 조직에서 나는 스스로 제대로 할 수 있는게 없었다.
그렇게 또 1년, 2년이 흐르니 내가 제일 잘난 놈이라고 생각되는 시기가 왔다.
이런 오만한 생각으로 6년을 한 부서해서 일했다.
그리고 이 시기에 과로가 누적되기도 했다.
S** 통신사의 시스템 업그레이드는 항상 새벽 2~4시 사이에 진행하는데,
엔지니어가 데이터센터(운영국사) 현장에서 주도적으로 작업하므로 내가 직접 개입할 일은 없지만
시스템 업그레이드 작업 중에 이슈가 발생하면 새벽 4시를 전후로 전화가 왔다.
엔지니어 입장에서는 예정된 작업 시간의 끝 무렵에 전화를 한 것이기 때문에 목소리에서 다급함이 느껴진다.
나는 집에서 졸린 눈을 비벼가며, 데이터센터의 운영 장비에 로그인해서 로그 내용과 설정 파일들의 이상 유무를 한번 더 점검해주곤 했는데
이것이 나의 건강을 조금씩 갉아먹고 있었나보다.
처음으로 마주한 건강의 무너짐. 천식과 마주하다.
그리고 모든 일이 그렇듯, 개발 프로젝트 일정도 빡빡해서 그 일정을 맞추느라 늦게 퇴근 하는 날이 많았다. (22~24시 퇴근이 많았음)
이런 밤/낮의 경계가 없는 생활을 5년쯤 했을 무렵, 천식이라는 병이 찾아왔다.
기관지가 쪼여오고 숨을 쉴 수 없는 생활이 지속되었고, 그래서 아침에 일어나면 내과 병원을 찾는 것이 일상이 되었다.
다행인 것은 매일 꾸준하게 호흡기 치료를 하고, 흡입기를 항상 들고 다닌 덕에 2년이 지날 무렵부터는 내복약없이 흡입기만 있으면 될 정도로 천식 증세가 호전되었다.
한번 건강을 잃고 나니, 모든 일을 대할 때 과로하지 않기 위해 조심하게 되었다.
나는 외벌이이고, 자식도 있고... 그래서 적어도 앞으로 15년은 더 경제활동을 해야 하니까, 건강 유지하면서 꾸준하게 페이스를 내자고 다짐하게 되었다.
3번째 조직으로 이동.
나는 5개월간 Network Procotol Stack만 개발하는 팀에 있었던 적이 있다.
그 팀에 합류하고 한달도 안 지나서 부서장과 마찰이 생겼다.
부서장과 문제의 근원은 아래와 같다.
팀장이 Network Procotol Stack을 개발하는 부서의 장인데, L2~L3 Network에 지식이 없었다. 그러다보니 내가 Layer2 ~ 3에서 동작하게 만든 구현체를 이해하지 못 했다. 설계 내용을 설명하거나, 소스 코드 리뷰를 할 때나, 상용 서비스에서 이슈가 생겼을 때 원인 분석 내용을 설명할 때 등 팀장은 어느 것 하나 이해를 못 했다. 팀장 본인이 이해를 못 하니 짜증이 났나보다. 언성이 높아지고 회의실에서 단둘이 있을 때는 폭력적인 행동까지 보였다.
내 마음이 너무 지쳐있었던 터라 내가 회사를 떠나는 것이 좋겠다는 판단이 섰다.
그래서 사직서를 내기로 하고, 임원에게 퇴사 면담을 했다.
임원에게 알린 나의 퇴사 사유는 "몸과 마음이 지쳐서 휴식이 필요함"이었다.
그런데...
4번째 조직으로 이동.
모든 Mobile network의 근간이 IP network으로 바뀌면서 음성 전화도 IMS의 일부 서비스인 VoIP(VoLTE)로 바뀌었다.
VoIP 분야는 개발 히스토리가 길어서 내가 VoIP 개발 부서로 갔을 때는 이미 대부분의 기능이 구현이 된 상태였다.
나는 기본 IMS 서비스에 Rich communication service를 추가로 기획/개발하는 업무를 맡았다.
새로운 세상을 열어간다는 생각에 일이 재미있었고, 새로운 ICT 기반 지식을 스터디하는 것도 흥미로웠다.
그렇게 1년 6개월을 밤/낮 구분없이 일하면서 보냈다.
세상이 또 한번 크게 변하려고 한다. 4G LTE, 스마트폰, 클라우드 컴퓨팅, AI, Machine Learning, Deep Learning ...