다시 오지 않을 그 날들...
불과 몇 년 전만 해도 새로운 개발 기법과 스킬을 익히는 과정은 순수한 즐거움이었다.
지독한 버그 하나를 잡으려 밤낮을 고민하고, 샤워하는 중에도 해결책을 떠올리며 괴로워하던 시간들.
하지만 마침내 문제를 해결했을 때 찾아오는 성취감은 내가 개발자로 살아가는 유일한 동력이었다.
새로운 기술이나 솔루션 세미나가 열리면 아무리 멀어도 달려갔고, 유명 개발자들이 모이는 곳이라면 어디든 찾아가 그들의 이야기에 귀를 기울였다.
그러나 이제 더 이상 그런 것들로 즐거움과 성취감을 얻을 수 없는 시대가 되었다.
내가 믿어온 세계가 산산이 부서지고 있는 느낌이다.
리눅스와 Git의 창시자인 리누스 토르발즈는 본인보다 AI가 코딩을 더 잘한다고 인정했다.
나는 이제 코딩 자체에서 성취감을 느끼기 어려워졌고, 코딩만을 업으로 삼기 힘든 시대를 마주했다.
나의 역할은 그간 쌓아온 경험을 바탕으로 무엇이 옳고 그른지 판단하는 '디렉터'의 영역으로 옮겨갔다.
하지만 문득 의문이 든다. 경험이 없는 이들은 이제 어떻게 해야 할까?
한때는 AI가 발달하면 경험 없는 이들이 세상을 살기 더 편해질 거라 믿었다.
선배에게 혼나가며 배우지 않아도, 무엇이든 친절하게 모범 답안을 일러주는 AI가 있다면 그곳이 곧 유토피아일 거라 생각했다.
하지만 현실은 정반대다. 경험 없는 자에게는 배움의 기회조차, 증명할 기회조차 주어지지 않는 냉혹한 사회가 되어가고 있다.
AI 시대, 나만의 포트폴리오를 위하여
예전에 근무했던 회사 중, 어떤 직장 선배는 한 직장에 오래 근속해야 진정한 프로이고 모범적인 직장인이라는 생각을 갖고 있었다.
그 선배는 경험이 많았고, 그 선배만큼의 경험을 가진 직원들이 회사에는 드물었기 때문에 나를 포함 대부분의 동료들은 자연스럽게 그 선배의 말에 동조했다.
하지만, 몇 년 지나지 않아 회사는 수익이 나지 않아 문을 닫았고, 직원들은 뿔뿔히 흩어졌다,
그 당시 여러 회사의 면접을 보았는데, 면접관들은 나에게 대부분 아래와 같은 질문을 했다.
- '4년 넘게 회사를 다녔는데 어떤 일을 하셨나요?'
하지만, 나는 그 질문에 '제품의 화면 대시보드를 개발했습니다' 라는 답 이외에는 아무 것도 말 할수가 없었다.
그 회사에서 경험했던 것이라고는, 임금체불, 직장 내 부조리 등 부정적인 경험 뿐이었는데 좋은 인상을 줘야 할 면접관 앞에서 그런 이야기를 할 수는 없기 때문이다.
아마 당시 함께 근무했던 동료들도 이직할 때 비슷한 일을 겪었을 것이다. 오래 근무한 직원일수록 면접이 더욱 힘들지 않았을까라는 생각도 든다.
그 사건을 통해 얻었던 교훈은 아래 3가지로 정리 할 수 있다.
- 버티는게 능사가 아니다.
- 나의 가치를 알아주는 사람과 일해야 한다.
- 회사는 나를 책임져주지 않는다.
그러나 배운게 도둑질이라고 사람의 습관이 어디가지 않는지 정작 다른 회사를 가서도 위 기준을 제대로 적용한 적은 없었다.
하지만 얼마 전 처음으로 위 기준을 제대로 적용한 결정을 내렸다. 1년간 재직했던 회사를 퇴사한 것으로.
만약 그 회사를 계속 다니고 있었더라면, 나는 어땠을까?
인간 개발자가 코딩하는 시대가 저물어가고 AI로 모든 것이 대체되고 있는 이 시대에, 이 흐름을 탈 수 있었을까?
결국 내 역할이 AI로 대체되고 나는 흐름에 뒤쳐진 채 다시 구직 시장에 나와야 했을 것이다.
AI 시대에는 단순히 '돌아가게끔 만드는 코딩'이나 유지보수 업무에 매몰된 개발자는 살아남기 어려울 것이다. 예전 회사에 계속 남아 있었다면 회사가 주는 일만 하며 스스로의 성장을 멈췄을 것이다.
과거에는 코딩하고 버그잡는 능력이 개발자의 특별한 능력이었다면, 지금은 더 이상 특별하지 않다. 이것을 받아들이지 못한다면 뒤쳐질 수 밖에 없다.
결국, 나의 경력은 회사가 부여한 직함이 아니라, 내가 스스로 쌓아 올린 포트폴리오로 말해야 한다.
지금 다니는 회사가 나의 성장을 저해하고 있다면,
그곳은 나를 위한 '포트폴리오'를 만들 수 없는 곳이다.
UPDATE 쿼리 실행 시 WHERE 절을 PK로 지정해야 하는 이유
대부분의 데이터베이스에서 특정 row의 데이터를 변경하기 위해 UPDATE ... SET .. WHERE ... 쿼리를 이용하여 데이터를 변경합니다.
WHERE 절에는 row가 특정될 만한 조건을 넣어야 하는데, PK(Primary Key)를 조건으로 지정하는 것이 일반적입니다.
오래된 코드에서는 개발자의 성향이나 편의 등의 사유로 WHERE 절에 PK가 아닌 컬럼을 조건으로 지정하는 경우를 종종 볼 수 있습니다.
예를 들어 아래와 같은 데이터가 있다고 가정해 보겠습니다.
id는 PK로 지정되어 있고, 나머지 컬럼은 모두 index가 생성되어 있다고 전제하겠습니다.
| id | code | category_id | name | origin |
|---|---|---|---|---|
| 200 | P202309261343597462 | 3 | Audra Kuhn | soluta |
| 153 | P202309261343597056 | 3 | Orlando Sipes | recusandae |
| 146 | P202309261343591320 | 3 | Brett Davis PhD | quia |
| 106 | P202309261343583007 | 3 | Orlando Shanahan | sed |
| 48 | P202309261343585376 | 3 | Larry Boyer | quidem |
| 38 | P202309261343573938 | 3 | Mossie Schumm | dolore |
| 31 | P202309261343575130 | 3 | Una Parisian | consequatur |
id 값이 146인 row의 name을 변경하려면 아래와 같이 SQL을 작성합니다
UPDATE tbl_name SET name = 'New Product' WHERE id = 146
아래와 같이 쿼리를 작성해도 동일한 결과를 얻을 수 있습니다.
UPDATE tbl_name SET name = 'New Product' WHERE category_id = 3 AND origin = 'quia'
첫 번째 쿼리와 두 번째 쿼리 모두 변경하고자 하는 row 만 제대로 특정된다면 크게 문제는 없어 보이는데요,
그럼에도 불구하고 두 번째 쿼리보다 첫 번째 쿼리를 사용하는 것이 좋은 이유는 무엇일까요?
바로 PK와 PK가 아닌 컬럼의 인덱스가 서로 다른 방식이기 때문입니다.
MySQL 기준, 테이블에 부여할 수 있는 인덱스의 종류로는 Clustered Index와 Non-Clustered Index 가 있습니다.
Clustered Index의 특징은 아래와 같습니다.
- 테이블당 1개만 지정 가능
- Primary Key 또는 Unique Index만 지정 가능하며, Primary Key가 있는 경우 PK가, 없는 경우 Unique Index가 Clustered Index 로 지정됨
- 물리적인 정렬 방식을 이용하여 SELECT 속도가 빠름 (UPDATE/INSERT/DELETE 속도는 느림)
- 생성시 데이터 재정렬이 필요하여 큰 부하 발생
반면에 Non-Clustered Index의 특징은 아래와 같습니다.
- 테이블당 여러개 지정 가능
- 페이지 단위로 인덱스가 저장되며, 랜덤한 순서로 저장 (뒤죽박죽)
- SELECT 속도보다 UPDATE/INSERT/DELETE 속도가 빠름
- 생성시 부하가 적어 비교적 부담 없이 생성 가능
트랜잭션 구간 내에서 UPDATE 쿼리 실행시 데이터베이스는 WHERE 절을 기준으로 레코드에 lock을 걸어 다른 트랜잭션에 의해 데이터가 오염되지 않도록 방지합니다.
Clustered Index를 기준으로 WHERE 절을 지정하면 단일 row만 lock이 걸리지만, Non-Clustered Index를 기준으로 WHERE 절을 지정하면 모든 WHERE 조건을 만족하는 1개 row만 lock이 걸리는게 아니라 WHERE 절의 첫 번째 조건을 만족하는 row 중 나머지 조건이 일치하는 row가 속한 인덱스 페이지 단위로 lock이 걸리게 됩니다.
아래는 Non-Clustered Index의 구조를 설명한 그림입니다.

MobileNo = 117인 row를 변경하려고 시도한다면, MobileNo = 117인 row 1개만 lock 이 걸리는 것이 아니라, 같은 인덱스 페이지인 115 ~ 118 범위의 row가 모두 lock이 걸리게 됩니다.
즉, 아래 쿼리로 UPDATE를 시도한다면, id = 146 인 row 1개만 lock이 걸리는 것이 아닌, category_id = 3인 row 중 랜덤한 row들이 lock이 걸리게 됩니다. (인덱스내 row는 정렬이 랜덤이라 페이지별 범위가 특정되지 않습니다)
UPDATE tbl_name SET name = 'New Product' WHERE category_id = 3 AND origin = 'quia'
실제로 변경하려는 row외 다른 row가 lock이 걸리는지 실험을 해보겠습니다.
DataGrip에서 Tx: Manual 로 설정 후 아래 쿼리를 실행하여 Lock을 겁니다.
SELECT * FROM products WHERE category_id = 3 AND origin = 'quia' FOR UPDATE
이 상태에서 id = 106 인 row의 데이터를 변경하려고 시도하면 Lock wait timeout exceeded 오류와 함께 변경에 실패하게 됩니다.
실제로 서비스중인 애플리케이션에 위와 같은 쿼리가 포함되었다면, Deadlock이 발생하여 전체 트랜잭션이 롤백되었을 것입니다. Non-Clustered Index 특성상 랜덤하게 페이지가 부여되기 때문에 항상 Deadlock이 발생하는 것이 아닌 '간헐적'으로 Deadlock이 발생하게 됩니다.
만약 운영중인 서비스에서 간헐적인 Deadlock이 발생하는데 원인파악이 어렵다면 위와 같은 케이스에 해당되지 않는지 면밀히 살펴보는 것이 좋습니다.
※ 본 포스팅을 할 수 있도록 조언과 도움을 아끼지 않았던 동료 개발자분께 감사드립니다.
Recent Comments