PR 올릴 때마다 손이 떨리는 이유

PR 올릴 때마다 손이 떨리는 이유

오늘도 PR 올렸다 키보드 앞에 앉았다. 손가락이 떨린다. Create Pull Request 버튼이 초록색으로 빛난다. 클릭하면 끝이다. 그런데 못 누른다. 3일 동안 짠 코드다. 로그인 페이지 리팩토링. 선배가 "이거 좀 정리해봐" 했던 건데, 막상 열어보니 스파게티 코드였다. 일단 작동은 한다. 로컬에서 10번 테스트했다. 근데 확신이 안 선다. 뭔가 놓친 게 있을 것 같다.체크리스트를 본다 노션에 적어둔 체크리스트를 연다. 로컬 테스트 완료 콘솔 에러 없음 코드 주석 달기 커밋 메시지 확인 PR 템플릿 작성다 했다. 근데 또 본다. 3번째다. 테스트는 했다. 근데 엣지 케이스는? 내가 모르는 케이스가 있을 수도 있다. 콘솔 에러는 없었다. 근데 warning은 2개 있었다. 괜찮은 건가. 나중에 물어봐야지. 주석은 달았다. 근데 너무 많나. 선배가 "주석 달지 말고 코드로 설명해" 했던 게 기억난다. 3개 지웠다. 커밋 메시지는 "feat: refactor login page UI"다. 괜찮은 것 같다. 근데 refactor인가 fix인가. 또 헷갈린다. PR 템플릿은 채웠다. "변경 사항", "테스트 방법", "스크린샷" 다 넣었다. 근데 너무 길다. 줄였다. 또 너무 짧다. 다시 늘렸다. 손가락이 계속 떨린다.마우스를 올린다 버튼 위에 마우스를 올렸다. 클릭만 하면 된다. 근데 못 한다. 머릿속으로 시뮬레이션한다.PR 올린다 슬랙에 알림 간다 선배가 본다 코드리뷰가 달린다여기서 멈춘다. 코드리뷰가 뭐가 올라올까. "이거 왜 이렇게 짰어요?" 이런 거. "useEffect 여기서 쓸 필요 없는데?" 이런 거. "타입 좀 제대로 써요" 이런 거. 다 맞는 말이다. 근데 매번 들으면 심장이 쿵 내려앉는다. 내가 모른다는 걸 또 들키는 기분이다. 물 한 모금 마셨다. 손은 여전히 떨린다.클릭했다 에라 모르겠다. 눌렀다. 초록색 버튼이 회색으로 바뀐다. "Pull Request #247 created" 심장이 빨라진다. 이제 돌아갈 수 없다. 슬랙을 연다. #dev-frontend 채널에 알림이 떴다. "이신입 님이 PR을 올렸습니다: feat: refactor login page UI" 선배 3명이 온라인이다. 누가 먼저 볼까. 5분이 지났다. 아무도 안 본다. 10분이 지났다. 김선배가 PR에 들어왔다. 빨간 점이 보인다. 손에 땀이 난다. 15분 지났다. 아직 코멘트 없다. 코드를 보는 중인가. 20분 지났다. 드디어 알림이 왔다. "Comment on your PR: 수고했어요. 몇 가지 코멘트 남겼습니다." 들어가본다. 코멘트가 7개다. 첫 번째: "여기 useState 초기값이 null인데 타입 명시 안 하셨네요" 맞다. 놓쳤다. 두 번째: "이 부분 조건문 중복인 것 같은데 함수로 빼면 어떨까요" 아. 그렇게 하는 거구나. 세 번째: "console.log 지워주세요" 아. 이런. 네 번째부터는 안 봤다. 일단 고쳐야 한다. 고친다 코멘트 하나씩 확인한다. useState에 타입 추가했다. useState<string | null>(null) 이렇게. 조건문은 validateInput 함수로 뺐다. 3줄이 1줄이 됐다. 깔끔하다. console.log 3개 지웠다. 디버깅할 때 찍어둔 건데 까먹었다. 나머지 4개도 다 고쳤다. 커밋 메시지는 "fix: apply code review feedback" 푸시했다. 다시 떨린다. 또 볼 건데. 또 뭐가 나올까. 30분 지났다. 김선배가 approve 눌렀다. "LGTM. 고생했어요" Looks Good To Me. 처음 봤을 땐 뭔 뜻인지 몰랐다. merge 버튼이 활성화됐다. 눌렀다. "Pull Request successfully merged and closed" 끝났다. 가슴이 내려앉는다. 후련하다. 근데 또 복잡하다. 왜 떨렸을까 매번 이렇다. PR 올릴 때마다 손이 떨린다. 머지되고 나면 괜찮다. 근데 올리기 전이 제일 무섭다. 뭐가 무서운 걸까. 틀릴까 봐. 모르는 게 들킬까 봐. 또 혼날까 봐. 다 맞다. 근데 제일 큰 건 이거다. 내가 잘하는지 모르겠다. 3일 동안 짠 코드인데, 이게 맞는지 확신이 없다. 작동은 하는데 잘 짠 건지 모르겠다. 선배들은 보면 안다. 한눈에 본다. "여기 이렇게 하면 안 돼요" 딱 집어낸다. 나는 모른다. 10번 봐도 모른다. 그게 무섭다. 코드를 올린다는 건 내 실력을 보여주는 거다. 숨길 수가 없다. "이 정도밖에 못 해요" 말하는 거랑 같다. 그게 떨리는 이유다. 그래도 올린다 근데 올려야 한다. 안 올리면 일이 안 된다. 코드는 혼자 짜는 게 아니다. 리뷰받아야 배운다. 혼자 공부하는 것보다 10배 빠르다. 김선배 코멘트 보면서 배웠다. useState 타입 명시하는 거. 조건문 함수로 빼는 거. console.log 지우는 거. 다 당연한 거다. 근데 나는 몰랐다. 이렇게 하나씩 배운다. 3개월 전 내 PR이랑 지금 PR 비교하면 다르다. 코멘트가 15개에서 7개로 줄었다. 그래도 떨린다. 여전히 떨린다. 근데 괜찮다. 이게 배우는 과정이다. 손 떨리는 게 부끄러운 게 아니다. 안 올리는 게 부끄럽다. 내일도 PR 올릴 거다. 또 떨릴 거다. 그래도 올린다.오늘 PR 7개 코멘트. 다음엔 5개로 줄여보자.

Next.js vs React, 신입은 둘 다 헷갈린다

Next.js vs React, 신입은 둘 다 헷갈린다

Next.js vs React, 신입은 둘 다 헷갈린다 면접 때 했던 말 "React 할 줄 아세요?" "네, 할 줄 압니다." 거짓말은 아니었다. 부트캠프에서 6개월 동안 React 했다. 컴포넌트 만들고 state 쓰고 props 넘기고. Todo 앱도 만들었다. 영화 검색 앱도 만들었다. 포트폴리오에 당당하게 썼다. 근데 회사 왔더니 Next.js다. "우리 Next.js 쓰는데, React 할 줄 알면 금방이에요." 선배가 웃으며 말했다. 나도 웃었다. 속으로는 'Next.js가 뭐지?' 했다.첫날 코드 열었을 때 /pages /api index.js _app.js _document.js이게 뭐지. 부트캠프에서 본 구조가 아니다. src 폴더는? components 폴더는? App.js는 어디 갔나? 선배가 설명했다. "pages 폴더에 파일 만들면 자동으로 라우팅돼요. /about 만들려면 about.js 만들면 돼요." "아... 네네." React Router 공부한 건 뭐지. useNavigate 쓰라고 했는데. BrowserRouter 감싸라고 했는데. 다 필요 없단다. getServerSideProps가 뭔데 export async function getServerSideProps(context) { const res = await fetch('https://api...') const data = await res.json() return { props: { data } } }선배가 보낸 코드다. "이 함수 안에서 데이터 받아오면 돼요. 서버에서 실행되니까 API 키 노출 안 돼요." 서버에서 실행된다고? 나는 useEffect 안에서 fetch 하는 것만 알았다. 컴포넌트 마운트될 때 데이터 받아오는 거. 그게 React 아닌가? "아 그건 클라이언트 사이드 렌더링이고, 이건 서버 사이드 렌더링이에요." 클라이언트... 서버... 렌더링... 부트캠프에서 들어본 것 같기도 하고.그래서 뭐가 다른데 퇴근하고 검색했다. "Next.js는 React 프레임워크입니다." React가 라이브러리면 Next.js는 프레임워크. 뭔 차이지. 더 검색했다.React: 라우팅 직접 해야 함 (React Router)Next.js: 파일 기반 라우팅React: 클라이언트 사이드 렌더링Next.js: 서버 사이드 렌더링, 정적 생성, 클라이언트 사이드 렌더링 다 됨React: SEO 약함Next.js: SEO 좋음음... 알겠는데 모르겠다. 결국 ChatGPT한테 물었다. "5살 어린이에게 설명하듯이 알려줘." 답변 읽었다. 여전히 모르겠다. 실무에서 느낀 차이 일주일 지났다. 태스크 받았다. "상품 상세 페이지 만들기." Next.js에서는 이렇게 했다.pages/products/[id].js 파일 만듦 getServerSideProps에서 상품 데이터 받아옴 컴포넌트에서 props로 받아서 렌더링끝. React였으면?컴포넌트 만들고 React Router로 라우팅 설정하고 useParams로 id 받고 useEffect에서 fetch하고 useState로 데이터 저장하고 로딩 상태 처리하고더 복잡하다. 그런데 Next.js가 쉬운 건 아니다. 뭔가 자동으로 해주는 게 많아서 이해가 안 된다. "왜 이렇게 돌아가는 거지?"를 모른다.헷갈리는 순간들 상황 1: 컴포넌트에서 라우터 쓸 때 // React import { useNavigate } from 'react-router-dom'; const navigate = useNavigate();// Next.js import { useRouter } from 'next/router'; const router = useRouter();비슷한데 다르다. 자동완성 믿고 쳤다가 에러 났다. 상황 2: Link 컴포넌트 // React <Link to="/about">About</Link>// Next.js <Link href="/about">About</Link>to인지 href인지 매번 헷갈린다. 둘 다 써봤다. 당연히 에러 났다. 상황 3: 이미지 최적화 // React <img src="/logo.png" />// Next.js <Image src="/logo.png" width={100} height={100} />Next.js는 Image 컴포넌트 쓰라고 한다. width, height 필수래. 왜 필수인지는 나중에 알았다. 최적화 때문이라는데. 처음엔 귀찮아서 그냥 img 태그 썼다. 선배가 PR에 코멘트 남겼다. "Image 컴포넌트 써주세요." 고쳤다. 선배들 대화 엿듣기 점심시간. "이번에 SSG로 바꿀까 SSR 유지할까?" "트래픽 보고 판단해야지. ISR도 고려해봐." "Vercel 배포하면 자동으로 최적화되니까 괜찮을 거야." 나는 김밥 씹으면서 고개만 끄덕였다. SSG? SSR? ISR? Vercel? 또 모르는 단어들이다. 퇴근하고 검색했다.SSG: Static Site Generation (정적 사이트 생성) SSR: Server Side Rendering (서버 사이드 렌더링) ISR: Incremental Static Regeneration (증분 정적 재생성)한글로 쓰니까 더 어렵다. 결국 깨달은 것 Next.js는 React를 포함한다. React로 할 수 있는 건 Next.js에서도 다 된다. useState, useEffect, 컴포넌트, props, 다 된다. 거기에 추가 기능이 있는 거다.파일 기반 라우팅 서버 사이드 렌더링 이미지 최적화 API 라우트 등등그래서 "React 할 줄 알면 Next.js도 할 수 있다"는 말이 틀린 건 아니다. 하지만 쉬운 것도 아니다. React 개념 제대로 모르는데 Next.js 기능까지 배우려니 머리 터진다. 지금 내 상태 입사 8개월. Next.js 프로젝트 3개 했다. 아직도 헷갈린다. getStaticProps와 getServerSideProps 언제 쓰는지 헷갈린다. dynamic import는 왜 쓰는지 모르겠다. prefetch는 뭔지 감도 안 온다. 그래도 한 가지는 안다. 면접 때 했던 "React 할 줄 압니다"는 거짓말이었다는 것. 제대로 아는 게 아니었다. 겉핥기였다. 지금도 마찬가지다. Next.js 쓰긴 쓰는데 제대로 아는 건 아니다. 선배가 짠 코드 복붙해서 조금씩 수정하는 수준이다. 신입의 생존법 모르면 물어본다. "이거 왜 getServerSideProps 쓰는 거예요?" "이 페이지는 왜 getStaticProps 안 쓰나요?" "Link 컴포넌트 prefetch 기본값이 true라는데 성능 괜찮나요?" 처음엔 물어보기 무서웠다. "이런 것도 몰라?" 할까 봐. 근데 선배들 반응은 의외로 괜찮다. "좋은 질문이네요. 왜냐하면..." 설명 들으면 50%는 이해된다. 나머지 50%는 나중에 이해된다. 모르는 건 노션에 적는다. "getServerSideProps: 매 요청마다 서버에서 데이터 받아옴. 실시간 데이터 필요할 때." 이렇게 쌓다 보면 언젠가 내 것이 될 거다. 아마도. 한 가지 확실한 것 React와 Next.js는 다르다. 비슷하지만 다르다. React는 라이브러리. 자유도 높다. 내가 다 설정해야 한다. Next.js는 프레임워크. 정해진 구조가 있다. 그 안에서 작업한다. 어느 게 더 나은가? 모르겠다. 회사가 쓰는 걸 쓴다. 신입은 선택권이 없다. 그냥 적응한다. 헷갈려도 계속 쓴다. 그러다 보면 익숙해진다. 아직은 헷갈리지만.면접 때 "Next.js 할 줄 아세요?" 물어보면 뭐라 대답할까. "React는 하는데 Next.js는 헷갈려요"가 정답일 것 같다. 솔직하게.

useEffect 의존성 배열 7번 물어본 것 같은데 아직도 헷갈려요

useEffect 의존성 배열 7번 물어본 것 같은데 아직도 헷갈려요

useEffect 의존성 배열 7번 물어본 그 이야기 매일 아침이 반복된다. 슬랙을 킨다. 밤새 쌓인 메시지 5개. 심장이 철렁한다. 그중 3개는 선배 메시지다. 읽는다. 다행히 급한 건 아니다. 커피를 마신다. 따뜻하지만 손이 떨린다. 오늘도 useEffect 때문에 버그가 터졌다. 7번 물어본 그놈 정확히는 몇 번인지 모른다. 5번? 7번? 10번? 세기 싫다. 선배가 얼굴을 어떻게 봤는지가 기억난다. 첫 번째는 입사 3주차였다. "선배, 이거... useEffect가 계속 실행되는데 왜 그래요?" "의존성 배열을 제대로 안 넣었어." 그렇다. 그걸 모르니까 물어본 거다. 배열. 뭘 넣어야 하는데? 코드를 보여줬다. 선배가 수정했다. 이렇게 넣으면 된다고. 머리로는 이해했다. 그날은. 그다음 주 월요일. 또 틀렸다. "선배... 이번엔 뭐가 문제인가요?" "의존성 배열이..." 선배 목소리의 톤이 달라졌다. 같은 실수를 두 번 하면 안 되는 건데. 안 했다. 다른 실수를 했다. 더 복잡한. 근데 정말 모르겠다. 왜일까.간단한 게 간단하지 않은 날들 "의존성 배열? 그거 간단한 건데." 선배가 입사 2개월 차 신입들 앞에서 말했다. 나는 그 신입들 중 한 명이었다. 간단한 게 아니었다. 의존성 배열이 간단한 이유를 다시 생각해보자. 뭔가가 바뀌면 효과를 다시 실행한다. 그게 전부다. 근데 그게 뭔가가 정확히 뭔가? 배열에 넣은 것들? 객체면? 함수면? 배열 안의 배열이면? 변수 이름을 user라고 하자. 이건 의존한다. user가 바뀌면 함수가 다시 실행된다. 당연하다. 그런데 user.name이 바뀌면? user 객체 자체는 같은데? "그건 React가 못 감지한다." 맞다. 객체 동일성 비교(참조 비교)로 작동한다. 그래서 불변성이고... 어? 불변성? 또 다른 용어가 튀어나왔다. 용어는 계속 튀어나온다. 배웠다고 생각했는데 진짜 간단한 게 아니었다. 배열에 아무것도 안 넣으면? 매번 실행된다. 빈 배열을 넣으면? 한 번만 실행된다. 의존성을 넣으면? 그것만 감지한다. 이건... 세 가지다. 세 가지를 모두 알아야 한다. 근데 셋 다 외워졌다. 8개월 전에. 왜 자꾸 틀릴까. 안 보이는 함수들 코드를 다시 봤다. useEffect(() => { fetchUser(userId) }, [userId])fetchUser는 밖에 정의되어 있다. 함수다. 그럼 이 함수도 의존성에 넣어야 하는가? 아니다. 상수니까. 바뀌지 않으니까. 근데 만약 이 함수가 userId를 가지고 있으면? 그럼 간접 의존성인가? 잠깐. fetchUser 자체가 useCallback으로 감싸져 있으면? 의존성 배열이 또 하나 생긴다. 함수 안에. 그 안에도 배열이. 배열이 배열 안에. 머리가 복잡하다. 종이에 그려본다. useEffect └─ useCallback └─ useEffect? └─ 의존성 배열?모르겠다. 회의실을 나온다. 밖에는 햇빛이 있다. 근데 뇌는 어둡다.콘솔로 찍던 날 어제도 이렇게 했다. useEffect(() => { console.log("이펙트 실행됨 1") return () => { console.log("클린업됨 1") } }, [A])useEffect(() => { console.log("이펙트 실행됨 2") return () => { console.log("클린업됨 2") } }, [B])콘솔을 켠다. 실행 순서를 본다. 뭔가 순서가 이상하다. 왜 2번이 먼저 실행되지? 아, 렌더링 순서와 상관없이 의존성 배열을 봐야 한다. 근데 이게 바뀌는 건가 안 바뀌는 건가? 콘솔에 값을 직접 찍어본다. A의 값. {id: 1, name: "kim"}. 근데 이 객체가... 매번 새로 생성되는 건가? const obj = { id: userId }; useEffect(() => { // }, [obj])이러면 매번 실행된다. 객체는 매번 새로 만들어지니까. 참조가 다르니까. 알았다. 근데 왜 자꾸 까먹지? 점심시간에 같은 회사 동기 3명이 편의점에서 만났다. 삼각김밥을 먹고 있었다. "너희도 useEffect 때문에 스트레스 받아?" "받는데... 의존성 배열?" "응." "나도. 자꾸 까먹어." "근데 선배들한테 물어보기가..." 아무도 마저 말을 안 했다. 다 알고 있었다. 물어볼 때마다 선배 얼굴이 어떻게 보이는지. 입사 8개월에 여전히 기본을 못 하는 게 맞는지 아닌지. 삼각김밥을 다시 먹는다. 맛은 없다. 인강은 또 다시 퇴근 후 휴대폰을 킨다. 인프런 앱을 연다. "React 완벽 마스터 - useEffect 이해하기" 섹션. 시간: 47분. 플레이를 눌렀다가 멈춘다. 47분을 언제 봐? 밤 11시다. 내일 9시에 출근해야 한다. 최소 6시간은 자야 한다. "3분만 보자." 3분 지난 강사. "의존성 배열이 중요한 이유는..." 벌써 여기서? 15초 후에 댓글을 본다. "와 이 설명 최고" 44개의 좋아요. 나는 왜 이렇게 못 이해하지. 다음 화면. 예시 코드. 이건... 아까 본 건 같은데 다르다. 변수 이름이 다르고 로직이 다르다. 5분 지나니 졸린다. 강사는 계속 말한다. "...그래서 의존성 배열에..." 그 말을 마지막으로 들었다. 아주 마지막으로. 그다음 기억이 없다. 휴대폰을 잡은 건 자정이었다. 선배 눈치 코드 리뷰가 왔다. 3개 파일. 내가 쓴 코드. 빨간 줄. 첫 번째가 useEffect 부분이다. 댓글: "@이신입 의존성 배열 검토 필요" "네 수정하겠습니다." 아 이 말. 이 말을 너무 많이 한다. 수정하겠습니다. 할 수 있는 말 중 제일 무난하다. 모를 때. 뭔가 잘못됐을 때. 선배가 뭔가 지적할 때. "수정하겠습니다." 수정한다. 의존성 배열을 고치려고 한다. 근데 뭘 고쳐야 하는지 불명확하다. 선배가 뭘 원하는 건지? 일단 추측해본다. 아 이거 빼야겠네. 이거 추가해야겠네. 맞췄나? 아니면 내가 또 놓쳤나? 슬랙에 답장을 기다린다. 한 시간. 아무것도 안 온다. 더 기다린다. 2시간. "이것도 추가해 주세요." 또? 의존성 배열이 4줄이 된다. useEffect(() => { // }, [a, b, c, d])이렇게 많으면 뭔가 잘못된 것 같은데? 모르겠다.과거와 현재의 간격 8개월 전. 부트캠프 마지막 주. 강사가 useEffect를 설명했다. 의존성 배열도. 나는 이해했다. 정말 정말 이해했다. 프로젝트를 해봤다. 기본 요청에 useEffect 썼다. 동작했다. "난 할 수 있을 것 같은데." 면접장 앞. 합격 통지를 받고. "React 좀 할 줄 아세요?" "네." 그건 정말이었다. 그 시점에는. 근데 회사는 달랐다. 변수가 많다. 의존성이 얽혀 있다. 함수가 함수를 부른다. 컴포넌트가 컴포넌트 안에 있다. 의존성 배열 경고가 나온다. ESLint가 불평한다. "exhaustive-deps" 뭔가 의존성을 빠뜨렸다는 뜻이다. 근데 그걸 다 넣으면 무한 루프가 된다. 그럼? 메모이제이션. useCallback. useMemo. 또 다른 Hooks가 튀어나왔다. 각각의 의존성 배열도 있다. 하나를 배워서 정복했다고 생각했는데 또 다른 게 나타났다. 그리고 또. 못 할 게 아니라 배우는 중이라고 자위 오늘도 새벽 3시. 노션을 켠다. "오늘 배운 것"의존성 배열 클린업 함수 참조 비교 불변성? (확인 필요)3일 전에 쓴 글. "내일은 정리해서 블로그에 올려야지." 블로그에 올린 글은 없다. 정리할 시간이 없었다. 내일도 없을 것 같다. 근데 누군가는 이걸 처음 배울 때 얼마나 헷갈렸을까? 인터넷 구석에 보면 이런 글들이 있다. "useEffect 의존성 배열 초보자 가이드 - 5분만에 이해하기" 5분만에 이해하는 사람이 있나? 있는 것 같다. 유튜브 댓글에서 봤다. "오 이제 이해됐어요!" 그런 댓글을 남기는 사람들은 뭐가 다를까. 더 똑똑한 건가? 아니다. 그냥 처음부터 맞게 배웠을 뿐이다. 난 부트캠프에서 45분 짜리 영상으로 배웠다. 강사는 빠르게 설명했다. 그리고 지금 이렇다. 내일이면 또 누군가가 나한테 물어볼 수도 있다. "선배, useEffect가..." 아. 그건 아직 먼 것 같다. 여기는 내가 그 위치니까. 그래도 어제보다는 어제 정말 난처했던 코드가 있었다. useEffect(() => { if (data && data.length > 0) { setProcessed(true); } }, [data, setProcessed])근데 생각해보니 setProcessed는 상태 업데이트 함수니까 의존성에 안 넣어도 된다는 걸... 오늘 깨달았다. 작은 거지만 뭔가 알아낸 기분이다. 선배가 가르쳐준 게 아니다. 어제 PR 피드백에서 본 다른 사람의 코드. 거기서 봤다. "아 이렇게 쓰는 거구나." 0.1%의 이해도가 올라갔다. 8개월에 0.1%씩 올라가면... 800개월이면 100%다. 너무 오래다. 근데 다른 신입들도 비슷한 것 같다. 우리는 같은 속도로 나아가고 있다. 느리지만. 정확히 같은 속도로. 내일 회의에서 누군가 또 useEffect를 쓸 것 같다. 그럼 또 의존성 배열 얘기가 나올 거다. 그걸 들으면서 조금 더 알게 될 거다. 그게 배움이다. 반복이다. 선배한테 8번 물어봐도 좋다. 아니다. 12번 물어봐도 좋다. 그래도 된다. 아마도.다음 PR은 좀 나아지겠지. 같은 실수를 세 번 반복하면 못 하는 건가? 아니다. 여덟 번 반복할 수도 있다. 그리고 9번째에 맞추면 된다.

매일 아침 슬랙을 두려워하는 이유

매일 아침 슬랙을 두려워하는 이유

매일 아침 슬랙을 두려워하는 이유 9시 10분, 의자에 앉기 전에 해야 할 일 출근한다. 키를 꺼낸다. 신발을 벗는다. 그리고 바로 핸드폰을 꺼낸다. 슬랙을 켠다. 왜 이렇게 했는지도 모르겠다. 자동으로 손가락이 움직인다. 마치 누군가 내 팔목을 조종하는 것처럼. 신발장에서 의자로 가는 5초 사이에 이미 확인해야 할 알림이 있는지 없는지가 결정되어 있다는 건 좀 이상하다. 하지만 지금 여기서 슬랙을 확인하지 않으면 뭔가 큰 일이 일어날 것 같은 기분이 든다. '안 읽은 메시지 12개' 심장이 철렁한다. 12개면 많은 건가. 적은 건가. 이게 정상인가. 다들 이렇게 받나. 몰라. 일단 의자에 앉자. 커피는 그 다음에.안 읽은 메시지의 심리학 안 읽은 메시지 개수가 눈에 띄는 순간. 뇌가 자동으로 시뮬레이션을 시작한다. '뭐가 잘못됐나.' '어제 올린 PR에 뭔가 이슈 생겼나.' '선배한테 처리하라고 했던 거 까먹었나.' 손가락이 떨린다. 진짜로. 노을색 아이콘이 떠 있는 슬랙 메시지들. 그걸 클릭하기 전까지는 상황이 정확하지 않다. 상황이 정확하지 않으면 대응할 수도 없다. 그래서 클릭하지 않은 채로 한참 동안 보고만 있다. '읽으면 끝나는데.' 맞다. 읽으면 끝난다. 그런데 읽기 싫다. 어제 날씨 얘기하는 메시지 한두 개는 괜찮다. 근데 그 사이에 '이신입, 요청 사항이 있어서' 이런 게 섞여 있으면? 죽는다. 슬랙에서 직급이 높은 사람의 이름이 떠 있으면 일단 불안하다. 그게 컨텍스트 없이 밤샘 중에 온 메시지라면 더욱 그렇다. 야간 메시지는 높은 확률로 급할 일이다. 야간 메시지가 지금 아침에 안 읽은 채로 남아 있다는 건 내가 이미 뭔가 늦었다는 뜻이다. '아... 네네. 죄송합니다. 지금 확인하겠습니다.' 이 문장을 몇 번이나 날렸는지 모른다. 안 읽은 메시지가 정말 무서운 건, 그게 뭘 요구하고 있는지 모른 채로 하루를 시작하게 된다는 거다. 마치 미션을 받기 직전인 것처럼. 또는 이미 미션을 받았는데 내가 못 들었던 것처럼. 어제 선배가 '이거 좀 부탁해'라고 메시지 남겼다면? 내가 이미 안 본 지 10시간이 지났으면? 선배는 지금 내가 한 줄 읽지 않았다는 걸 알고 있을까. 아니다. 선배는 이미 나를 잊었다. 그리고 1시간 뒤에 '왜 아직 안 했어?' 이러면 난 뭐라고 변명할 건가. '아... 죄송해요. 지금 봤어요.' 항상 이 문장이 나온다.12개의 메시지, 12개의 시나리오 슬랙을 켰을 때 첫 번째로 하는 일. 메시지를 분류한다. 머릿속으로.팀 공지 채널 → 읽어야 하는 건가. 다들 읽나. 바로 내 이름이 멘션된 거 → 죽는다. 스레드 안 메시지 → 아, 내가 답할 차례인가. 선배 개인 메시지 → 가장 무서운 거. 팀 전체 메시지 → 어 뭐라고? 자동화 알림 (예: 배포 성공) → 이건 안심이다.1~5번 중에서 1개라도 있으면 출근 기분이 싹 날아간다. '오늘도 바쁘겠네.' 대체로 그렇다. 안 읽은 메시지가 5개 이상이면 확정. 오늘은 뭔가 처리해야 할 일이 있다. 그게 뭔진 아직 모르지만 분위기상 그렇다. 가장 무서운 건 메시지 제목이 모호할 때다. '한번 봐줄 수 있어?' 이거면 뭘 봐야 하는 거야. PR? 코드? 로직? 디자인? 결과물? 아니면 지금 화면을 공유한 채로 있는 건가. 슬랙에는 창사 이래로 가장 모호한 질문들이 쌓여 있다. '우리 이거 어떻게 할까?' 누가 "우리"야. 내가 한 건가. 선배가 한 건가. 다 함께? 'PR 올려줄래?' 언제까지. 오늘. 내일. 지금. 답변 없이 마침표로 끝나는 메시지는 정말 악질이다. 그건 거의 명령이다. 질문처럼 보이는 명령. 구두점 없는 메시지도 불안하다. 뭔가 급한 느낌이 난다. '이거 빨리' 빨리가 얼마나 빨리인지는 명시 안 하고. 나는 매일 이런 메시지들을 해석하는 데 10분을 쓴다. 그리고 그 해석이 틀렸을 확률은 60%다. 선배에게 물어보면 된다. '선배, 이거 뭐 하라는 거예요?' 근데 이미 바쁜데 이렇게 물으면 선배도 스트레스 받을까봐 묻지 못한다. 그래서 혼자 추리한다. '아마도 이런 뜻인 것 같은데... 내 생각이 맞으면 좋겠어.' 틀렸다. 항상 틀렸다. 그리고 다시 물어본다. 그리고 'PR 다시 올려줄래?'라는 메시지가 온다.읽기 전에 이미 피곤함 슬랙을 켜는 순간 하루가 시작된다. 정확히는 하루가 결정된다. '안 읽은 메시지 0개' 이러면 뭔가 이상하다. 뭔가 빠진 게 있나. 다들 내 얘기를 안 했나. 자유로운 아침이 오면 그게 더 불안하다. 조용함이 불안하다. '안 읽은 메시지 3개' 보통. 이 정도면 조기 중퇴까지는 아니다. 점심까진 버틸 수 있을 것 같다. '안 읽은 메시지 8개 이상' 오늘은 지옥이다. 실제로 읽어보니 그 중 5개가 일반 공지고 2개가 옛날 메시지 캐시였던 일도 있다. 하지만 그건 읽기 전엔 모른다. 읽기 전엔 모두 폭탄처럼 보인다. 심장이 미리 뛴다. 커피를 마셔도 소용없다. 이미 카페인은 필요 없다. 불안감만으로 충분히 깨어 있다. 손이 떨린다. 자판을 잘못 친다. 코드를 쳐야 하는데 자꾸 슬랙 창으로 넘어간다. '또 뭐 왔나.' 또 왔다. 또 온다. 매 시간마다. 가장 많이 오는 건 오전 10시와 오후 2시다. 그 두 시간이 지나면 안심한다. '오늘은 별로 없겠네'라고 생각한다. 하지만 3시쯤 되면 다시 온다. 퇴근 30분 전 쯤에 가장 무서운 메시지가 온다. '내일 아침까지 이거 한번 봐줄 수 있어?' 내일 아침. 즉, 오늘 밤 내 시간이라는 뜻이다. 퇴근 후 1시간은 자유 시간이지만 사실상 그 시간도 빼앗긴다. 왜냐면 메시지가 와도 답장을 하지 않으면 마음 한구석이 계속 불편하기 때문이다. '내일 아침'은 절대 내일 아침이 아니다. 그건 '오늘 밤'이다. 아무도 이 암호를 가르쳐주지 않았는데 나는 알고 있다. 부트캠프에서 배운 것도 아니고. 그냥 실전에서 습득했다. 신입들의 강제 학습 과정. 선배는 몰라도 되는 것들 선배한테 물어봤다. '선배는 아침마다 슬랙 안 읽은 거 보고 불안하지 않아요?' 선배가 웃었다. '신입이니까 그래. 나도 처음엔 그랬어.' 그게 위로인지 협박인지 모르겠다. '이거 저한테 온 거 맞죠?' '응. 너한테.' 그럼 왜 9시간이 지나도록 답장이 없으면서... 아. 나는 그런 부분은 신경 쓰지 않는 거구나. 신입이 아니니까. 선배는 슬랙을 켜긴 하는데 보는 게 아니라 흘려본다. 쭉 지나간다. 마치 뉴스를 보는 것처럼. 그리고 중요한 거만 나중에 처리한다. 근데 내건 전부 중요한 것 같다. 왜냐면 뭐가 중요한지 내가 아직 모르니까. 동기 재준이는 어제 메시지에 답장을 2시간 뒤에 했다. '나 그냥 1시간에 한 번 체크해.' 1시간에 한 번. 신세한 거다. 재준이도 불안한 건 같은데 걔는 좀 더 여유 있어 보인다. 왜 난 안 된다고 생각할까. 아마도 내가 자존감이 낮아서 그런 것 같다. 모르는 게 많아서. 언제라도 혼날 준비가 되어 있어서. 선배는 충분히 알아. 그래서 메시지를 들을 필요가 없다. 근데 나는 부족해. 그래서 매 메시지가 시험처럼 느껴진다. '이거 할 수 있어?' 이 질문도 사실 '할 수 있어?'가 아니라 '바로 해야 해?'라는 뜻일 거다. 근데 '네' 하고 못 하면? 미친다. 마지막으로 남겨진 것들 지금 시간 9시 14분. 내가 슬랙을 켠 지 14분. 여전히 12개가 남아 있다. 이제 읽어야 한다. 그런데 한 가지 확실한 건, 그걸 읽고 나면 내 아침은 끝난다. 아침이 끝나고 나머지 하루가 결정된다. 혹시 모르니까 화장실부터 다녀올까. 그것도 시간 낭비다. 결국엔 봐야 한다. 좋아. 켈 게. 손가락이 미끄러진다. 첫 번째 메시지. '우리 이거 어떻게 할까요?' 다른 선배다. 다른 팀이다. 관계 없는 안내다. 다음. 'ㅂ1대신 ㅂ2로 수정해줄 수 있어?' 이게 뭔데. ㅂ은 뭐고 어디서 수정하는 거야. 다음. '✓' 이건 뭐지. 승인이야. 거절이야. 내 심장은 계속 뛴다. 나머지 9개를 계속 읽는다. 대부분 별 거 아니다. 그냥 공지. 그냥 조회. 하지만 그 안에 한두 개가 나를 지목한다. 그게 오늘 하루를 결정한다. '이신입, 어제 올린 거 확인해봤어?' 피한다. '아 네. 지금 보겠습니다.' 지금 본다. 에러가 있다. 고칠 수 있나. 못 고칠 것 같은데. 선배한테 다시 물어봐야 하나. 물어본다. '그거 왜 이렇게 됐어요?' '너가 처음부터 잘못 이해했나 봐. 다시 해봐.' 다시 한다. 하루가 이렇게 간다.결국 슬랙은 내 아침을 훔쳐간다. 그리고 아침은 하루를 훔쳐간다.

9시 10분 출근의 심리학: 신입 개발자의 눈치 문화

9시 10분 출근의 심리학: 신입 개발자의 눈치 문화

9시 10분 출근의 심리학: 신입 개발자의 눈치 문화 매일 아침 알람은 8시 20분에 울린다. 샤워하고, 옷 입고, 편의점에서 아메리카노 한 잔 사고, 버스 탈 시간을 계산한다. 정확히 8시 50분쯤 회사 건물 지하에 도착한다. 그다음부터는 심호흡을 한다. 사실 나는 8시 50분부터 9시까지 10분간 건물 로비 의자에 앉아 있다. 아무것도 안 하고 그냥 앉아 있다. 왜 이러는 걸까? 이건 정말 이상한 버릇인데, 내가 유일한 사람은 아닌 것 같다. 지난주에 우리 팀 인턴 최준호가 비슷하게 하는 걸 봤다. 9시 정각에 들어갈 용기가 없어서 9시 15분에 들어왔다. 엘리베이터에서 만났을 때 우리 눈이 마주쳤고, 우린 말 없이 고개를 주억였다. 그 순간 알았다. 이건 나만의 문제가 아니라 우리 회사의 문제, 아니 이 직장 문화 전체의 문제라는 걸.첫 출근부터 시작된 마음 속의 전쟁 8개월 전, 입사 첫날 이른 아침 6시에 깼다. 잠을 못 잤다. 부트캠프에서 6개월을 붙어 있다가 드디어 '진짜 직장'에 들어가는 거였다. 면접 때 "React 좀 할 줄 아세요?"라는 질문에 나는 당당하게 "네"라고 대답했다. 지금은 그 대답이 제일 크고 싶은 거짓말이다. 하지만 그날은 몰랐다. 나는 할 수 있다고 생각했다. 첫날 사무실에 도착한 게 8시 45분이었다. 일찍 도착해서 좋은 인상을 주고 싶었다. 신입 개발자의 열정이 그 정도였다. 그런데 사무실을 열고 들어갔을 때 깜짝 놀랐다. 아무도 없었다. 책상들 앞에 아무도 앉아 있지 않았다. 나는 한 시간을 혼자 사무실에서 보냈다. 모니터를 켜고, 마우스를 집어 들었다가 놨다. 메모장을 열었다가 닫았다. 정말 할 게 없었다. 9시가 되자 사람들이 들어오기 시작했다. 그 다음이 문제였다. 들어오는 순서가 있었다. 팀장이 먼저, 그 다음 3년 차 선배 김태호, 5년 차 선배 이준수. 그리고 맨 마지막에 막내들이 들어온다. 아, 그리고 정확히 9시에 들어오는 사람은 거의 없다. 다 9시 5분, 9시 10분, 심하면 9시 20분. 나는 8시 45분에 들어왔으니까 되게 열심히 보이는 거겠지 하고 생각했다. 점심시간이 되자 팀장이 불렀다. "이신입, 너 어제부터 있는 거 알지? 첫날부터 그렇게 일찍 올 필요 없어. 아침에 푹 자고 와. 여긴 그런 회사 아니야." 나는 "네, 감사합니다"라고만 했다. 그 순간부터 뭔가 헷갈렸다. 9시 정각이 정시인데 왜 일찍 오지 말라는 거지? 그럼 몇 시에 와야 하는 거야? 이게 눈치인 건가? 혼자 생각했다. 그걸 팀장에게 물어볼 수는 없었다. 슬랙 메시지가 울리는 순간의 공포 이제 내 일과는 이렇게 시작된다. 9시 정각이 되기 5분 전부터 나는 노트북을 켜서 슬랙(Slack)에 들어간다. 최대한 자연스럽게. 마치 이미 와 있었던 것처럼. 누가 진짜 먼저 슬랙에 연결되는지는 알 수 없지만, 마음 속으로는 "내가 먼저 왔는데?"라는 심리가 작동한다. 그리고 9시 10분쯤 사무실에 정말로 들어간다. 엘리베이터를 타면서 마음을 진정시킨다. 아무것도 아닌 것처럼. 사실 내 심장은 두근거린다. 오늘도 또 모르는 게 있을까? 오늘도 또 코드리뷰에서 야단 맞을까? 누군가 나를 보고 있지는 않을까? 사무실에 들어가면 제일 먼저 슬랙의 안 읽은 메시지를 확인한다. 이 순간이 제일 무섭다. 빨간 숫자가 떴다는 건 뭔가 놓쳤다는 뜻이고, 그건 내가 '게으른 신입'이라는 뜻이고, 그건 결국 3개월 뒤 수습 평가 때 내가 탈락한다는 뜻이다. 논리적이지 않다는 걸 알지만, 새벽 4시에 눈이 떠질 때 이 생각이 제일 먼저 난다.오늘 아침에도 슬랙에 메시지가 3개 있었다. 팀장: "좋은 아침이에요. 다들 오늘 미팅은 11시에 있습니다." 이건 괜찮다. 이준수 선배: "신입, 어제 PR 올린 거 봤어? 코멘트 달았어. 확인하고 수정해 줄 수 있어?" 이건 공식적인 업무니까 괜찮다. 그런데 마지막 메시지는... 김태호 선배의 '반응만'이었다. 내가 어제 올린 PR 코멘트에 👍를 눌렀다. 그건 뭐지? 좋다는 뜻? 아니면 "이 정도는 기본이지"라는 뜻? 나는 이 반응의 의미를 해석하기 위해 5분을 소비했다. 결국 결론은 "아마 좋다는 뜻일 것 같은데, 혹시 모르니까 오늘은 특히 더 잘 보여야겠다"였다. 9시 10분에 사무실에 들어오는 게 정말 중요한 이유가 여기 있다. 너무 이르면 '열심히 하려고 애쓰는' 신입으로 보이고, 정각에 들어가면 '규칙을 정확히 지키는' 신입인데 그건 뭔가 서툰 느낌이 나고, 너무 늦으면 게으른 신입이 된다. 9시 10분은 딱 맞는 타이밍이다. 9시 정각이 아니라는 건 '너는 우리 문화를 알고 있다'는 뜻이고, 10분 정도라는 건 '너무 뻔한 건 아니다'는 뜻이다. 이건 신입이 할 수 있는 최적의 눈치다. "이거 간단한 건데" 함정에 빠지다 어제 일은 정말 최악이었다. 오전 11시 미팅이 있다고 했는데, 미팅은 10시 50분에 시작됐다. 회의실에 들어갔을 때 모두들 이미 앉아 있었다. 나는 제일 마지막에 들어갔다. 그리고 제일 어색한 자리에 앉았다. 팀장 옆. 팀장이 나를 보면서 웃었다. "신입, 너 요즘 기분은 어때?" 모두가 날 본다. 뭔가 심사하는 눈빛이다. 나는 "좋습니다"라고 했다. 더 이상 뭐라고 답할 수 없었다. 미팅 내용은 새로운 프로젝트였다. 우리가 하던 대시보드를 리팩토링하는 건데, 이번엔 Next.js를 쓸 거라고 했다. 나는 React를 쓰다가 Next.js는 처음이었다. 하지만 입 밖으로 꺼내지 않았다. 그냥 고개를 끄덕였다. 끄덕, 끄덕. 이건 내 버릇이 됐다. 모든 회의에서 나는 끄덕인다. 이해가 안 되면 더 끄덕인다. 미팅이 끝나고 이준수 선배가 나한테 왔다. "신입, 너 이 작업 하나 해 줄 수 있어? 사실 간단한 건데..." 그 순간 나는 알았다. '간단한'이라는 단어가 나올 때가 가장 위험하다는 걸. 선배들에게 '간단한'은 결코 간단하지 않다. '간단한 건데'는 '너 같은 신입이 이 정도는 할 수 있겠지?'라는 의미다. 그 작업은 폼(form) 밸리데이션이었다. 사용자가 입력한 데이터를 검증해서 서버로 보내는 기능. 나는 바로 "네, 제가 해볼게요"라고 대답했다. 큰 실수였다. 들어가서 코드를 봤을 때, 그건 정말 '간단한' 수준이 아니었다. TypeScript 제너릭(Generic)을 써야 했고, 커스텀 훅(custom hook)도 만들어야 했고, 에러 핸들링까지 정교하게 해야 했다.나는 ChatGPT에 물었다. "폼 밸리데이션 React 코드 만들어 줄 수 있어?" 그리고 그 결과물을 그대로 복붙했다. 에러가 났다. 타입 에러였다. any를 쓰면 되겠지 했는데, 그 코드엔 any가 이미 10개 있었다. 나는 거기에 any 5개를 더 추가했다. console.log를 10개 찍으면서 뭐가 문제인지 찾으려고 했다. 3시간이 지났다. 이준수 선배가 등에 손을 얹었다. "신입, 뭐 하고 있어? 진행 상황 어때?" 나는 심장이 철렁 내려앉았다. 완성 못 했다는 뜻인가? 아니면 진짜 상황을 물어보는 건가? 둘 다 위험했다. "아... 네네, 이거 좀 복잡한데요. 다시 해볼게요."라고 했다. 내 입버릇인 "아... 네네"가 나왔다. 이건 내가 불안할 때 무조건 나오는 말이다. 선배가 내 화면을 봤다. console.log 10개가 보였다. 그리고 any 15개가 보였다. "아, 그냥 제가 다시 한번 봐 줄까?" 나는 "감사합니다!"라고 했다. 속은 다르지만, 목소리는 감사함으로 가득 찼다. 선배가 5분 만에 수정했다. 정말 간단했다. 나는 그 코드를 다시 읽었다. 공부를 해야겠다고 생각했다. 하지만 퇴근하면서도 인프런에 접속하지 않았다. 눈치의 악순환: 누가 정말 책임일까 지금 생각해 보면, 이 9시 10분의 버릇은 단순히 출근 시간 문제가 아니다. 이건 눈치 문화 전체를 상징한다. 우리 회사는 정식으로 "9시에 출근"이라고 한다. 그런데 실제로는 아무도 9시에 안 온다. 팀장은 9시 5분, 이준수 선배는 9시 20분, 김태호 선배는 9시 15분. 그리고 신입인 나는... 9시 10분. 이게 처음엔 회사 규정이 이상하다고 생각했다. 왜 9시에 오지 말라고 하면서 9시를 정시라고 하는 거야? 그런데 지금은 알 것 같다. 이건 회사가 유연하다는 걸 보여주는 방식인 거다. "우리는 시간이 없어도 돼"라는 의사 표현인 거다. 근데 그 유연함이 오히려 더 큰 압박이 된다는 걸 회사는 모르는 것 같다. 신입 입장에서 보면, 9시 정각에 들어갈 수가 없다. 왜냐하면 그건 "나는 회사 문화를 모르고 규칙만 따르는 사람입니다"라는 신호이기 때문이다. 그렇다고 9시 20분에 들어가면? 그건 "나는 게으릅니다"라는 신호다. 9시 10분은 "나는 당신들이 뭐를 하는지 알고 있습니다. 그래서 적당하게 늦습니다"라는 신호다. 이건 신입이 생존하기 위해 배워야 하는 문법이다. 근데 정말 웃긴 건, 이 문법이 실제로 일의 질과는 아무 상관이 없다는 거다. 9시 10분에 들어온 신입이 더 좋은 코드를 쓰지는 않는다. 더 빨리 일을 끝내지도 않는다. 단지 "눈치가 있다"는 평가를 받을 뿐이다. 그리고 눈치가 있다는 평가가 회사에서 제일 중요한 능력인 것 같다. 코딩 실력? 그건 나중이다. 눈치가 있고 없고가 먼저다. 나는 정말 지쳤다. 매일 아침 10분 동안 건물 로비에 앉아 있는 이 시간이. 심호흡을 하고 마음을 준비하고 '지금부터 연기를 시작해야지'라고 생각하는 이 시간이. 그런데 그렇게 할 수밖에 없다. 왜냐하면 다른 사람들도 다 그렇게 하니까. 그리고 그렇게 하지 않으면 탈락할까봐서. 어제 부트캠프 동기들 단톡방에서 물었다. "너네 회사는 어때? 신입이라고 괜찮아?" 받은 대답은 다 비슷했다. "여긴 9시 정시인데 아무도 9시에 안 와. 왜 이러지?" "우리 회사도 그래. 정시가 뭐지?" "좋아. 그럼 우리 다 똑같네." 이상한데, 이게 이상하다는 걸 모두가 아는데도 바뀌지 않는다. 신입들은 계속 눈치를 본다. 선배들은 계속 그걸 당연하게 본다. 그리고 회사는 "우린 자유로운 회사야"라고 자랑한다. 아무도 이 악순환을 끝낼 생각이 없다. 그래도 내일 아침에도 내일 아침도 알람은 8시 20분에 울릴 거다. 나는 또 8시 50분에 건물 로비에 도착할 거고, 9시 10분에 사무실에 들어갈 거다. 아무것도 바뀌지 않을 거다. 왜냐하면 이게 이미 내 몸에 배인 습관이 됐으니까. 신입이라는 신체가 눈치를 본다는 신호를 받으면 자동으로 반응한다. 근데 언젠가 나도 이준수 선배처럼 9시 20분에 들어올 수 있을까? 그때가 되면 이 모든 불안이 사라질까? 아니면 그때쯤엔 새로운 신입에게 같은 걸 하게 될까? 정말 답답하지만, 그래도 내일은 좀 나아지겠지.눈치 본다는 건 결국 내가 이곳에 속하고 싶다는 뜻이기도 하다.