Amazon 출신 엔지니어가 Principal(수석) 엔지니어/사이언티스트 역할에 대해 롤모델들로부터 관찰하고 배운 내용을 31가지 조언으로 정리한 글
프린시펄은 사실상 제품·디자인·엔지니어링·문화 전반을 다루는 다역할자로, 판단력으로 폭넓은 업무를 수행함
핵심은 코딩보다 기술 비전, 설계 피드백, 조직 연계이며, 이전 역할에서 핵심이었던 작업은 이제 부차적 업무가 됨
조직이 가치를 두지 않는 것에 가치를 가르치는 일이 큰 부분을 차지하며, 단순히 옳은 판단을 하는 것보다 타인이 공감하고 행동하게 만드는 설득력이 중요함
프린시펄 없이는 일어나지 않을 작업 카테고리에 집중하고, 직접 실행보다 타인을 연결하고 육성하는 것이 더 가치 있을 수 있음
자율성과 책임이 함께 오며, 조직이 집중해야 할 문제를 스스로 찾아야 하고, 필수 경로에서 벗어나 인접한 위치로 전환해야 장기적으로 효과적
1-3. 역할의 본질과 스타일
1번: 프린시펄마다 서로 다른 스타일 존재
한 분야 깊이 파고드는 타입 vs 수평적 영향력 뛰어난 타입
기술적 선구자 vs 복잡성 명료화 타입 vs 여러 조직을 공통 비전으로 정렬하는 타입
자신의 강점에 맞는 스타일을 찾아야 하며, 어떤 스타일도 다른 것보다 더 중요하지 않음
Amazon은 프린시펄이 hands-on으로 일하기(직접 참여)를 명시적으로 요구하며, 장기간 hands-on이 아닌 경우 실패 가능성이 높음
2번: 이전 역할에서 핵심이었던 작업이 이제는 부차적 업무
직접 코드 작성이 시간의 최선 활용이 아닐 수 있으나, 작업과의 연결 유지를 위해 여전히 코딩 필요
핵심 역할은 기술 비전, 설계 피드백, 후원, 비즈니스·제품·기술 컨텍스트 제공, 새로운 문제 발견, 연결 등
L7+ 역할의 잘 알려진 이야기: 80% 시간을 코딩에 쓰더라도, 가장 임팩트 있는 부분은 모든 사람을 더 효과적으로 만드는 것
단일 프로젝트 코딩 집중에서 모든 빌더가 더 잘 빌드하도록 영향력 미치는 것으로 마인드셋 전환
고품질 코드를 리포지토리에 기여하고 코드가 스스로 말하게 하는 것뿐만 아니라, 설계 제안 피드백, 기술 가이드 작성, 장기 비전 주도 등 다른 노력들이 더 효과적일 수 있음
3번: 기술을 위한 파트타임 PM, 실제로는 모든 것에 대한 파트타이머
제품, 디자인, 엔지니어링, 과학, QA, 채용, 재무, 문화 등 모든 영역
“당신의 일이 아닌 것은 없음”
높은 판단력으로 전문 영역 밖으로 나갈 수 있음
4-7. 커뮤니케이션과 영향력
4번: 당신의 역할은 더 많은 커뮤니케이션, 영향력, 적절한 사람을 연결하는 것을 포함
프로젝트는 일반적으로 범위가 크고, 디렉터와 VP를 넘나드는 팀들 포함
효과적인 정렬과 협업 없이는 성공 불가능
조직도를 고객에게 전달하지 않도록 주의 필요
5번: 옳은 것만으로는 절반도 안 됨
다른 사람에게 옳다는 것을 설득하고, 더 중요하게는 행동하도록 충분히 신경 쓰게 만들어야 함
모멘텀 구축 방법, 후원자 찾기, 완수 방법 파악 필요
때로는 프로젝트를 시작하고 가치를 보여줘야 이를 얻을 수 있음
프린시펄은 모범을 보여야 하며, 사람들이 위원회를 기다리지 않고 문제 해결에 적극적이길 원함
“프린시펄이 바로 위원회인데, 누구를 기다리나요 :)”
6번: 조직이 가치를 두지 않는 것에 가치를 가르치는 것이 업무의 큰 부분
청중은 임원부터 실무 IC(Individual Contributor)까지 범위에 포함됨
가장 어려운 작업이며 자주 실패하지만, 미래를 보고 더 넓은 시야를 가진 사람으로서 여전히 해야 함
멘토는 10개 문서 제안 중 약 3개가 실행되면 훌륭한 결과로 간주
일상 팀 레벨 업무를 담당하는 IC와 달리, L7+ 역할은 회사의 더 넓고 장기적인 관점 취할 공간 보유
임팩트를 더 객관적으로 평가 가능하며, 그 레벨에서 기여하는 것이 가장 가치를 더함
7번: 프린시펄 없이는 일어나지 않을 작업 카테고리가 존재함
일반적으로 자신이 정말 관심 있는 것과 탁월한 것의 교차점에 위치
빠른 프로토타입 구축하고 리더십과 새로운 고객 경험 공유, 조직 간 또는 업계 다른 실무자들과의 다리 구축, 3년 비전 작성 등
이 카테고리 작업에 집중해야 함
경력이 진행되면서 더 깊고 좁아짐
8-11. 타인을 통한 확장
8번: 가장 가치 있는 일은 직접 작업하는 것이 아니라 연결하는 것
작업하려는 팀을 이미 작업한 팀과 연결하여 재사용하거나 학습하게 함
작업 수행 가능하고 그 과정에서 성장할 적합한 사람 식별 포함
9번: 혼자 할 수 있는 것은 한계가 있음
다른 사람을 코칭하고 멘토링하여 더 효과적이도록 하는 데 시간 투자가 조직에 더 유용
매주 몇 시간(오피스 아워 또는 정기 싱크) 할애
육성하고 싶은 1-2명의 IC 식별하고, 어떻게 도울지 목표 설정
타인을 통한 확장이 핵심: PE(Principal Engineer)의 성공은 조직이 PE와 동일한 결정을 내릴 수 있을 때
PE는 다른 모호한 문제로 이동하고 올바른 결과를 달성하는 문화 설정
모두를 돕고 싶지만, 장기적으로 자신의 자리를 대체할 수 있는 사람 구축에 에너지 집중 필요
그런 능력을 보이는 모든 사람에게 집중된 시간을 투자해야 하며, 그들이 다시 현재는 잠재력이 덜 보이는 다른 사람들을 도울 수 있음
10번: 타인을 작업에 참여시키는 것에서 작업을 그들의 것으로 만드는 것으로 전환
누군가가 자신을 현재 위치에 도달하게 한 작업을 할 기회로 만듦
지원하고 성공을 위한 설정 제공
고객을 위한 중요한 문제와 흥미로운 기회의 끝없는 백로그가 있으므로 걱정 불필요
주당 1-2시간을 누군가와 보내며 그들이 자신의 통찰력 하에 40시간 작업 달성하도록 확장하는 것이 진정한 프린시펄 스케일
11번: 타인에게 작업을 줄 때, 이제 그것은 그들의 것
컨텍스트와 가이드 제공 가능하지만, 궁극적으로 방향 설정은 그들의 몫
자신이 취하지 않을 접근법을 취하도록 허용 포함
잘못되면 모두 배우고, 잘되면 자신이 배울 수 있음
단, 프로젝트가 역효과를 낼 수 있는 고위험 일방향 문으로 걸어가는 경우 개입 필요
12-15. 회의와 참여 관리
12번: 회의에서 타인을 위한 공간 창출
때로 회의실은 가장 시니어한 사람의 의견이나 결정을 기대, 질문하여 타인을 위한 공간 창출 가능
참여하지 않는 사람이 보이면, 그들의 강점에 맞는 주제로 부드럽게 끌어들임
회의가 논의에 있어야 할 누군가를 잊었다면, 다음 회의에 추가
13번: 항상 가치를 입증할 필요 없음
가장 효과적인 프린시펄들은 회의 내내 침묵하거나 문서 리뷰에서 거의 코멘트를 남기지 않음
팀과 논의가 잘 진행되고 있다면 훌륭함
작업 흐름에서 한 걸음 물러나 다른 곳에 에너지 집중 가능
주의: 참석하고 침묵하면 암묵적 승인의 의미, 멀티태스킹과 참석의 의미 주의
14번: 임원과의 회의에서 안건의 모든 주제를 다룰 필요 없음
임원들이 주제에 몰입하고, 의미 있는 질문하고, 그들만이 할 수 있는 결정을 내리거나 장애물 제거하면 좋은 회의
15번: 폭넓은 역할에서 일하면 전체 주간이 들어오는 모든 것으로 가득 찰 수 있음
리뷰, 에스컬레이션, 이메일, 도움 요청 등 포함
조직에 오래 머물수록 더 큰 문제가 될 수 있으며, 많이 알거나 신뢰를 얻어 “go-to” 사람이 됨
결과적으로 모든 회의의 “필수” 참석자가 됨
시간을 지키는 법을 배우지 않으면 정말 관심 있는 아이디어를 추진할 시간이 없음
모든 회의에 있거나 모든 아이디어에 의견을 가질 필요 없음, 중요한 것만
멘토가 공유한 것: 생각할 시간은 꼭 필요함, 회의에서 회의로 가면 처리하고 앞을 볼 수 없음
다음 큰 것을 찾기 위해 질 높은 사고 시간 스케줄하고 회의에서 연결 해제 필요
위임할 방법을 찾아야 함: 다른 사람을 그 사람으로 설정하여 시간 확보, 자신을 자유롭게 하는 이점뿐만 아니라 새로운 사람이 성장하도록 돕는 범위 마련
16-19. 임팩트와 커뮤니케이션
16번: 작업 중인 것에 프린시펄이 왜 필요한지 설명 못하면 잘못된 것에 작업 중일 수 있음 (L6에도 적용 가능)
17번: 지위로 인해 때로는 상대적으로 적은 노력으로 결과 개선 가능
타이틀은 조직적 특권을 부여하며, 관계와 컨텍스트에 대한 더 큰 접근 제공
경험과 결합하여 모퉁이를 더 잘 볼 수 있음
따라서 상당히 적은 노력 투자로 프로젝트의 성공 가능성과 결과를 의미 있게 개선 가능
높은 ROI이며, 이런 기회를 발견하면 행동
18번: 타이틀은 가지지 말아야 할 때도 신뢰성의 아우라 제공
때로 사람들은 자신의 즉흥적 코멘트를 과도하게 해석, 특히 잘 모르는 경우
결과적으로 자신이 한 캐주얼한 코멘트 때문에 많은 작업 수행 가능
시간과 노력 낭비 가능
따라서 아는 것, 모르는 것, 요청하는 것, 단순히 코멘트하는 것을 명확히 해야 함
19번: “무엇”만 말하지 말고, “왜” 그렇게 생각하는지도 공유
다른 사람이 더 나은 결정을 내리도록 도움
다른 사람들이 “프린시펄 <이름>이 이렇게 말했으므로 우리는…”이라고 말하며 완전히 이해하지 못하고 따라하는 것을 줄임
L7 입력이 필요한 문제는 보통 상당한 불확실성 하의 결정 포함
중요하지만 어려운 것: 정신 모델 명확히 표현하기 – 모든 정보 없이 판단에 도달하는 방법, 왜 특정 지식이 다른 데이터 포인트보다 중요한지
20-24. 조직과의 연결 유지
20번: 팀과 연결을 유지할 메커니즘 찾기
설계 리뷰, 주간 데모, 근처에 앉아 듣기, 팀 점심, 캐주얼한 복도 대화 등
조직의 맥박을 유지하고 주요 문제나 기회가 어디에 있는지 파악하는 데 도움
21번: 팀이 더 큰 그림을 계속 보도록 도움
실무 레벨이 일의 두꺼운 부분과 일상 전달에 집중할 때, 때로 더 크고 장기적인 문제/기회를 놓치고 지역 최적에 갇힐 수 있음
보유한 컨텍스트로 팀에게 이를 상기시키는 데 도움 가능
22번: 실용적이어야 함; 큰 그림 보기와 지역 솔루션 수용 균형
세부 사항에 대해 실무 레벨과 상담하고 경청, 그들이 현장 전문가
23번: 한두 시간만 함께한 사람에 대한 리뷰나 승진 피드백 요청받을 수 있음
전체 행동의 작은 샘플에 기반한 낮은 품질 피드백 제공하는 대신 거절 가능
24번: 인턴과 멘토와 상호작용할 시간 만들기
인턴십 동안 몇 번의 접점이 혁신적일 수 있음
초기 체크인(필요시 방향 수정), 데모 데이 참석 포함
멘토, 인턴과 함께 인턴십 이후에도 계속 가치 있는 결과물 향해 작업
제품 1-페이저, 작동하는 소프트웨어, 기술 문서 포함
25-28. 프린시펄로서의 전환과 책임
25번: 프린시펄에 도달하려면 필수 경로에 자신을 놓아야 함. 프린시펄로서 효과적이고 그 이상으로 가려면 적극적으로 자신을 제거해야 함
이전에는 “go-to” 사람이었지만, 필수에서 인접으로 전환 필요
조직은 점점 더 자신으로부터 혜택을 받아야 하지만, 효과적이기 위해 의존해서는 안 됨
자신이 만드는 기여를 다른 사람이 할 수 있도록 권한 부여 방법 생각
필수 경로 프로젝트에 자신을 주입하는 것 주의: 집중이 다른 우선순위에 의해 빼앗기기 쉬우므로, 필수 경로에서 벗어나거나 필수 경로에 있다면 정말 엄격하게 고정 필요
26번: 프린시펄로 승진했다면 이미 한동안 프린시펄로 행동해왔기 때문
일반적으로 1년 이상
따라서 타이틀의 증가된 기대에 대해 걱정 불필요
하던 것을 계속하고, 다른 프린시펄들과 교류하고, 스타일 파악하고, 리더십과 협력하여 집중 영역 식별
27번: 큰 자유에는 큰 책임이 따름
작업할 것을 선택하는 자율성이 있지만, 책임과 임팩트에 대한 기대 존재
자유는 원하는 것을 하는 것이 아니라, 해결할 가장 높은 레버리지 문제를 찾는 소유권에 관한 것
무엇을 하라고 들을 것으로 기대하거나 어떤 가이드를 받을 것으로 기대하지 말 것
조직이 집중해야 할 것을 파악할 것으로 기대됨
28번: 리더십과 헌장 정의 및 정렬
한 가지 방법: 작업을 세 버킷으로 분할 – (i) 소유자, (ii) 후원자, (iii) 컨설턴트
컨설턴트: 리뷰에 참여하고 가이드 제공, 시스템이나 제품 의도에 대한 높은 수준 이해
후원자: 위 내용 외에도 아이디어를 조직의 우선순위로 만들고, 정렬 구축과 결정 주도를 위해 작업하며, 이해관계자와 교류
소유자: 위 모든 것 외에도 시스템 전문가이자 첫 번째 연락 지점이며, 설계, 실행, 임팩트의 성공에 대한 거의 집착 수준 보유
본인은 보통 1-2개 프로젝트 소유(>50% 시간), 2-3개 프로젝트 후원(~20% 시간), 나머지 시간 컨설팅
29-31. 개인 성장과 지속가능성
29번: 프린시펄이 되는 것은 외로울 수 있음
모든 팀의 일부이지만 또한 어느 팀의 일부도 아님
열린 대화를 나눌 수 있는 동료 네트워크 구축
같은 회사나 도메인에서 일하는지는 중요하지 않을 가능성
30번: 자신의 필요를 무시하지 말 것
학습, 성장, 웰빙을 지원하는 프로젝트를 위한 시간과 공간 만들기
단기적으로는 이기적으로 느껴질 수 있지만, 조직에서 번아웃되는 것보다 훨씬 선호됨
자신을 건강하고 행복하고 성장하게 유지하는 작업을 적극적으로 찾으면 조직도 혜택을 받으며, 조직이 자신을 유지하기 더 쉬움
매니저와 협력하여 균형 방법 파악
31번: 계속 학습할 것; 업계는 빠르게 움직임
아무것도 가르쳐주지 않거나 최소한 작업과 관련 없는 것을 가르치는 프로젝트를 맡으면 뒤로 가는 것
때로는 불가피하며, 그런 프로젝트가 오면 타임박스 설정
학습이 직무에서만 올 필요 없음
논문과 기술 교과서를 읽고, 주말에 프로토타입을 해킹하여 새로운 아이디어와 기술을 더 잘 이해하는 시간을 찾는 PE들 존재
파이토치에서 학습 루프를 정의하는 건 보다 간단하고 직관적이다. 변수를 생성하고 동적 그래프를 그때그때 정의하면서 모델을 학습할 수 있다. 데이터와 타겟을 디바이스(CPU/GPU)에 할당하고 순전파를 연산한다.
모델이 순전파(feed-forward) 연산을 마치고 나면, pytorch의 사전 정의된 개체들을 통해 역전파 과정을 수행할 수 있다. 그래디언트를 계산하고, 역전파 방법론을 적용함으로써 파라미터를 업데이트 한다.
for epoch inrange(epochs):
for batch, (data, target) inenumerate(train_loader):
# cuda 파라미터 가져오기
data = data.to(device=device)
target = target.to(device=device)
# 순전파
score = model(data)
loss = criterion(score, target)
# 역전파
optimizer.zero_grad()
loss.backward()
optimizer.step()
4. MNIST로 파이토치 이해하기
필요한 라이브러리를 불러온다
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt
나는 언젠가 분명 ‘명령적(Imperative) 프로그래밍 vs 선언적(Declarative) 프로그래밍’에 대해 들어본 적이 있다.
저게 무엇을 의미하는지를 당연히 검색해봤지만, 그 때마다 요런 정의 정도만 접해볼 수 있었다.
명령형(절차적) 프로그래밍은 당신이 어떤 일을 어떻게 할 것인가에 관한 것이고,
선언적 프로그래밍은 당신이 무엇을 할 것인가에 관한 것입니다.
만약 내가 명령형과 선언형의 차이를 이미 알고 있었더라면 이 정의가 명확하게 다가왔겠지만 나는 그렇지 않았다.
사실 나 뿐만 아니라 많은 사람들이 이 주제에 대해 어려움을 느끼는데,
아마 직관적으로는 무슨 말인지 알지만, 누군가에게 설명할 만큼 명확하지는 않기 때문일 것이다.
많은 개발자들이 여기기에 가장 효과적인 방법은 비유와 실제 코드를 조합해서 설명해주는 것이었다고 하니,
해당 방법으로 ‘명령형 vs 선언적 프로그래밍’이라는 주제를 다뤄보도록 하자.
우선, 위에서 언급했던 저 ‘어떻게, 무엇을’에 관한 정의는
사실 ‘명령형 vs 선언형’의 핵심을 담고있다고 봐도 무방하다.
이를 이해하기 위해, 프로그래밍의 관점에서 잠깐 벗어나 현실에서의 예시를 들어보도록 하자.
■ Red Lobster
당신은 회사에서 너무 오랜 시간 자바스크립트를 다루느라 피곤해졌습니다.
그리고 이를 달래기 위해 퇴근 후에 아내와 함께 ‘Red Lobster’ 식당에 근사한 데이트를 하러 갔습니다.
당신은 Red Lobster에 도착했고, 프론트 데스크에 가서 다음과 같이 말했습니다.
명령형 접근(HOW): “저기 Gone Fishin’ 이라고 적힌 표지판 아래에 있는 테이블이 비어있네요.
우리는 저기로 걸어가서 저 테이블에 앉도록 하겠습니다.”
선언형 접근(WHAT): “2명 자리 주세요.”
명령형 방식은 내가 실제로 자리에 어떻게 앉을지에 관심이 있다.
이를 위해 나는 내가 어떻게 테이블을 잡아서 자리에 앉을지에 관해, 필요한 단계들을 하나하나 나열해야 한다.
반면, 선언형 방식은 오로지 내가 무엇을 원하는지에 관심이 있다. 여기서 말한 ‘두 명을 위한 테이블’ 처럼.
■ Wal-Mart
친구가 당신의 집에 집들이를 오기 위해 Wal-Mart에서 선물을 샀습니다.
현재 친구는 Wal-Mart 바로 옆에 있으며, 당신의 집에 어떻게 도달해야 하는지를 전화로 물어봅니다.
이에 관한 명령형 대답과 선언형 대답을 모두 생각해보세요.
명령형 접근(HOW): “주차장 북쪽 출구로 나와서 좌회전을 해. 12번가 출구에 도착할 때까지
I-15 북쪽 도로를 타고 와야 해. 거기서 IKEA에 가는 것처럼 출구에서 우회전을 해. 그리고 거기서
직진하다가 첫 번째 신호등에서 우회전을 해. 그 다음에 나오는 신호등을 통과한 후에 좌회전을 하면 돼.
우리 집은 #298 이야.”
선언형 접근(WHAT): “우리 집 주소는 298 West Immutable Alley, Eden, Utah 84310 이야.”
추가로 한 가지 비유를 더 하자면, 수동 스틱 자동차와 오토 스틱 자동차를 예로 들 수 있다.
친구가 ‘우리 집에 어떻게 도착하는가’ 와는 별개로, 친구가 어떤 차를 운전하느냐에 관한 이야기이다.
수동 스틱(1종)은 명령형 방식이고 오토 스틱(2종)은 선언적 방식이다.
만약 당신이라면 어떤 차를 운전하겠는가?
실제 코드로 예시를 들기 전에, “자리에 앉는 방법은 누가 알지?”,
“주소는 아는데, 집에 가는 방법은 누가 알지?”와 같은 의문이 생길 수 있을거라 생각한다.
이에 대한 대답은, 선언적 방식의 접근을 위해서는 명령형 방식으로
‘어떻게 접근하는가’에 관한 내용이 먼저 추상화 되어있어야 한다 라는 것.
Red Lobster 직원에게 사용했던 선언형 접근(“2명 자리 주세요.”)에는, Red Lobster 직원이
‘테이블에 어떻게 앉는가’에 관한 모든 명령형(절차적) 단계들을 알고 있다는 가정이 뒷받침되어 있다.
친구에게 우리 집의 주소를 알려주는 것도, 친구가 ‘우리 집에 어떻게 도착할 수 있는가’에 관한 명령적 절차들을
모두 알고있는 일종의 GPS 같은 것을 가지고 있다는 것을 전제로 한다.
오토 스틱(2종) 자동차는 변속 기어에 대해 일종의 추상화 계층(Layer)을 가지고 있다.
위의 내용들을 다음의 문장으로 다시 한 번 정리할 수 있다.
많은 선언적(Declarative) 접근 방식들의 기반에는 일종의 ‘명령적(Imperative) 추상화’가 존재한다.
이제, 여러가지 비유로 떡칠된 예시들을 졸업하고 현실 세계의 코드 예시를 살펴볼 차례이다.
그 전에, ‘선언적 프로그래밍 언어’와 ‘명령적 프로그래밍 언어’에는 어떤 것들이 있는지 간단히 살펴보자.
명령적 언어: C, C++, Java
선언적 언어: SQL, HTML
(Can be) Mix: Javascript, C#, Python
우선, 대표적인 선언형 언어인 SQL과 HTML의 코드를 살펴보자.
SELECT * FROMUsersWHERE Country='Mexico';
<article><header><h1>Declarative Programming</h1><p>Sprinkle Declarative in your verbiage to sound smart</p></header></article>
이 두 가지 예시 모두 문법만 알고 있다면, 어떤 일이 일어나고 있는지 명확히 알 수 있다.
이 둘은 모두 선언형 프로그래밍이며, 어떤 일을 어떻게 수행하는가 보다는 무엇을 수행하는가에 관심이 있다.
즉, 우리가 무엇을 얻고자 하는가에 관해서만 묘사하고 있고, 그것을 어떻게 얻는가에 관해서는 알려주지 않는다.
SQL 예시에서는 멕시코에 거주하는 모든 유저들을 선택하는 ‘방법에 대한 구현’은 우리에게서 추상화되어있다.
HTML 예시에서는 ‘웹 브라우저가 어떻게 article 엘리먼트를 파싱해서 화면에 보여주는가’는 고려하지 않는다.
우리의 WHAT은 오직 ‘멕시코 유저들‘과 웹사이트의 ‘header와 paragraph‘이다.
지금까지는 충분히 알아먹을 수 있을 것 같다.
이제, 조금 더 실전적인 자바스크립트 예제로 들어가보자.
■ 기술 면접
당신은 현재 기술 면접을 보는 중이고, 나는 해당 기술 면접의 interviewer입니다.
콘솔창을 열고, 제가 묻는 질문에 해당하는 답을 작성해보세요.
1. 숫자 배열을 받아서, 해당 배열의 모든 원소들을 두 배 시킨
새로운 배열을 리턴하는 ‘double’이라는 이름의 함수를 작성하세요.
ex) double([1, 2, 3]) // [2, 4, 6]
2. 숫자 배열을 받아서, 해당 배열의 모든 원소들을 더한 값을 리턴하는 ‘add’라는 함수를 작성하세요.
ex) add([1, 2, 3]) // 6
3. jQuery나 Vanilla Javascript를 이용해서, btn이라는 id를 가진 엘리먼트에 이벤트 리스너를 달아보세요.
해당 버튼을 클릭했을 때 highlight라는 class를 toggle(add or remove)해야 하고,
엘리먼트의 현재 상태에 따라 버튼의 텍스트를 ‘Add Highlight’와 ‘Remove Highlight’로 바꿔야 합니다.
이 문제들에 대해, 가장 흔히들 작성하는 ‘명령적’ 코드들을 먼저 살펴보자.
functiondouble (arr) {
let results = []
for (let i = 0; i < arr.length; i++){
results.push(arr[i] * 2)
}
return results
}
functionadd (arr) {
let result = 0for (let i = 0; i < arr.length; i++){
result += arr[i]
}
return result
}
$("#btn").click(function() {
$(this).toggleClass("highlight")
$(this).text() === 'Add Highlight'
? $(this).text('Remove Highlight')
: $(this).text('Add Highlight')
})
무엇이 이 코드들을 명령적으로 만드는지 알기 위해서는, 이 세 가지 코드들에서 공통점을 뽑아내야 한다.
1. 가장 명백한 공통점은 이들이 어떤 일을 어떻게 처리하는지에 관해 묘사하고 있다는 것.
각각의 예시에서, 우리는 명시적으로 배열을 반복하거나(for 문),
우리가 원하는 기능을 수행하기 위한 단계들을 명시적으로 나열하고 있다.
2. 각각의 예시에서 우리는 ‘상태(state)의 일부’를 변경하고 있다.
(상태: 메모리에 저장되어 있는 것들에 대한 정보. 변수와 비슷하다고 생각하면 됨)
처음 두 예시에서는 ‘results’라는 변수를 만들어 그것을 계속해서 수정하고 있다.
세 번째 예시에서는 아무런 변수도 없지만, 여전히 DOM 자체에 state가 존재하고 있다.
그리고 해당 코드는 DOM의 state를 수정하고있다.
3. 약간 주관적이지만, 위의 코드들은 가독성이 떨어진다.
위의 코드들을 한 번 슥 훑어보고 어떤 일이 일어나고 있는지 바로 알아채기는 쉽지 않을 것이다.
우리의 뇌는 코드가 존재하는 맥락을 고려해가며, 해당 코드들을 인터프리터처럼 차근차근 살펴봐야 한다.
처음 두 예제에서, 자바스크립트의 내장 함수인 map과 reduce를 활용한 레버리징에 주목하자.
이는 이 글 내내 반복해서 언급했던,
‘가장 효율적인 선언적 프로그래밍 방법은 명령적으로 작성된 코드를 추상화하는 것이다‘
라는 것에 관한 내용이다.
해당 예제들은 우리가 어떻게 처리할지보다, 무엇을 원하는지를 설명한다.
(우리는 map과 reduce가 어떻게 구현되어있는지는 전혀 알지도 못하고, 관심도 없다)
우리는 아무런 state도 변경하지 않는다. 모든 변경들은 map과 reduce 내부에 추상화되어있다.
그리고 당신이 map과 reduce에 익숙하다는 가정 하에, 훨씬 더 가독성이 좋다.
이제, 마지막 예제를 살펴보자.
여기서는 약간의 편법으로 리액트를 사용했다.
그러나 중요한 것은 명령형의 세 가지 문제점들이 모두 해결이 되었다는 것.
리액트의 진정한 강점은 이러한 선언적인 방식으로 UI를 작성할 수 있다는 것이다.
우리의 Btn 컴포넌트를 보면, 해당 UI가 어떤 식으로 보일지를 빠르게 알아챌 수 있다.
또 다른 강점은 state가 DOM에 존재하는 대신, 우리가 만든 리액트 컴포넌트 자체에 존재한다는 것.
선언적 프로그래밍의 또 다른, 덜 알려진 장점 중의 하나는
우리의 프로그램이 context-independent 해질 수 있다는 것이다.
(전체적인 맥락, 상황에 좀 더 독립적이다)
선언적 코드들은 최종적인 목표가 무엇인지에 대해서만 관심이 있지,
해당 목표를 이루기 위한 세부적인 단계들(해당 목표에 의존적인 과정들)에는 관심이 없다는 것.
그래서 동일한 코드들이 다른 프로그램에 쓰이더라도 정상적으로 동작할 수 있게 된다.
당장 위의 세 예시만 보더라도, 저 함수들이나 컴포넌트는
우리가 만들고자 하는 어떤 프로그램에 갖다붙여도 정상적으로 동작한다.
저들은 현재 어떤 프로그램에 속해있는가 하는 것에 전혀 구애받지 않는다.
명령형 코드들은 그렇지 못한 경우가 많은데, 그 이유는 대부분의 경우
명령형 코드들이 현재 상태의 컨텍스트에 의존적이기 때문이다.
(그래서 다른 곳에서 재사용하기가 어렵다)
이 글의 저자는, 선언적 프로그래밍에서 더 나아가 함수형 프로그래밍까지 다뤄보라고 권유하고 있다.
map, reduce, filter를 사용해보면서 차근차근 시작하라고 하는데, 함수형 프로그래밍에 관해서도
조만간 한 번 알아보는 시간을 가지면 좋을 듯.
개인적으로 가장 와닿았던 부분은, 선언적 프로그래밍은 절차적 구현을 추상화함으로써 이루어진다는 것.
선언적 프로그래밍을 어떻게 해야 하는가에 대해 좀 더 감을 잡을 수 있게 해준 내용인 듯.
리뷰어분들께서 말씀하시는 ‘재사용성’이나 ‘순수 함수’에 관한 것들이 약간 일맥상통하는 부분이 아닌가 싶다.
최대한 독립적이고 재사용성이 높은 함수들을 구현하여, 해당 함수들의 조합으로 프로그래밍을 구성하면
전체 코드의 유지보수가 쉬워지고 가독성도 훨씬 높아지겠지. 그게 말처럼 쉽겠냐마는..
아래는 저자가 웹을 돌아다니며 발견한 선언적 프로그래밍의 또 다른 정의들.
Declarative programming is “the act of programming in languages that conform to the mental model of the developer rather than the operational model of the machine.”
선언적 프로그래밍은 “기계의 동작을 모델로 하는 것이 아닌, 개발자의 두뇌(정신, 생각)를 모델로 본딴 언어를 가지고 프로그래밍 하는 것” 이다.
Declarative Programming is programming with declarations, i.e., declarative sentences.
선언적 프로그래밍은 선언(선언적 문장, 선언문)들을 사용해서 프로그래밍하는 것이다.
?
The declarative property is where there can exist only one possible set of statements that can express each specific modular semantic. The imperative property is the dual, where semantics are inconsistent under composition and/or can be expressed with variations of sets of statements.
선언적 속성이란, 특정 모듈을 설명하는 문장 집합이 단 하나만 존재할 수 있다는 것을 의미한다. 명령적 속성은 이중적이고, 이는 의미들의 구성에 일관성이 없고 여러 가지 문장 집합들로 표현될 수 있다는 것을 의미한다.
Declarative languages contrast with imperative languages which specify explicit manipulation of the computer’s internal state; or procedural languages which specify an explicit sequence of steps to follow.
선언적 언어는 컴퓨터의 내부 상태를 명시적으로 조작하는 명령형 언어, 또는 밟아야 하는 일련의 절차들을 명시적으로 지정하는 절차적 언어와 반대된다.
In computer science, declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow.
CS에서 선언적 언어는 제어 흐름을 설명하지 않고, 계산 로직을 표현하는 패러다임(개념)이다.
I draw the line between declarative and non-declarative at whether you can trace the code as it runs. Regex is 100% declarative, as it’s untraceable while the pattern is being executed.
나는 코드의 실행을 추적할 수 있는지 여부에 따라 선언형과 비선언형을 구분한다.
정규표현식(Regex)은 패턴이 실행되는 동안 그것을 전혀 추적할 수 없기 때문에 100% 선언적이라고 할 수 있다.
+ Zereight’s Blog에서 본 인상적인 설명
명령형 프로그래밍은 문제를 어떻게 해결해야하는지 컴퓨터에게 명시적으로 명령을 내리는 방법을 의미하고,
선언형 프로그래밍은 무엇을 해결할 것인지에 보다 집중하여 어떻게 문제를 해결하는지에 대해서는
컴퓨터에게 위임하는 방법이다.
처음 프로그래밍이라는 개념이 등장했을 떄부터, 지금까지도 컴퓨터에게 명시적으로 명령을 내리는 방법인
명령형 프로그래밍을 주로 사용했지만, 함수형 프로그래밍은 문제를 해결하는 방법에 더 집중하고
사소한 작업은 컴퓨터에게 넘겨버리는 선언형 프로그래밍의 일종이다.
즉, 컴퓨터에게 사소한 작업들을 위임해버리는 패러다임의 특성 상
선언형 프로그래밍에는 필연적으로 높은 수준의 추상화라는 키워드가 붙는다.
추상화 수준이 낮다면 저 사소한 작업들을 개발자가 일일이 다 컨트롤해야한다는 의미이기 때문이다.