해당 방법론은 1990년 후반 켄트 백의 익스트림 프로그래밍의 일부이다(근데 왜 난 20년이 지나 공부하는가 ㅡㅡ ㅋㅋㅋㅋㅋ). 우리의 프로젝트에 TDD 방법론 적용은 오버 엔지니어링이라고 생각할 수 있지만, 단위 테스트 코드의 작성을 습관화하는 게 좋지 않을까 생각한다.
해당 서적에서도 TDD 인강에서도 TDD는 불안, 두려움을 지루함으로 바꾸는 방법론이라고 다들 얘기한다. 필자 또한 전적으로 동의한다. 서비스 운영 및 배포를 하면 장애가 항상 수반하는데, 배포했을 때 "과연 장애가 없을까..?", "제대로 배포가 됐을까?"와 같은 불안을 최소화 시켜준다. 이와 같은 작업을 반복하면서 안전한 서비스를 만드는 것이다. TDD가 100% 모든 테스트를 대신하는 것이 아니며, 100% 안전한 서비스를 제공하는 수단이 아니지만 많은 비중을 차지하는 것은 서적과 필자의 경험으로 얘기할 수 있다. TDD에 회의감을 가지는 많은 개발자가 있지만 적당선의 타협을 하면 개발 리소스 감축할 수 있는 효과를 볼 수 있다.
간략하게 요약하면 기능 변경에 따른 회귀 테스트를 통해 회귀 버그를 최소화할 수 있다. 회귀 버그로 인해 개발 리소스 비용 증가는 모두가 아는 사실이다(저 보라색 비용을 다들 싫어하더라.. 그래프에는 보라색이 작게 표시 되었지만 실제는 더 크다.) 서스테이닝이 없는 단발성 프로젝트는 엄격한 테스트는 굳이 필요 없지만, 서스테이닝 요하는 서비스는 필수라고 생각한다. 현재 프로젝트는 통합 테스트, 기능 테스트만 제대로 적용됐다. 이 두 개의 테스트로도 효과는 많이 보고 있지만 단위 테스트를 "제대로" 적용하면 더할나위 없다고 본다. 몇 개 로직에 단위 테스트를 적용하여 효과를 봤기 때문에 확언할 수 있다.
보통의 개발자, 자신들의 이야기이다. 그들의 열정과 간절함을 느낄 수 있었다. 비전공자이지만 자신의 특장점을 잘 살려서 해외에서 개발자가 된 친구, 전략적으로 산업체에서 개발자로 일한 친구, 관련 학과지만 많은 고민 끝에 개발자가된 친구 등.. 해당 친구들의 발자취를 간접적으로 볼 수 있어서 영광이었다.
성장하고 싶은 주니어 개발자, 성장하고 있는 주니어 개발자, 성장했던 시니어 개발자, 모두 이 책을 보면 좋겠다. 올챙이는 다른 올챙이를 보고 간접경험을 충분히할 수 있고, 개구리는 자신의 올챙이 시절 치열함과 열정을 다시 불태울 수 있다. 필자는 크고있는 올챙이지만 어린 올챙이 시절의 간절함을 다시 회고할 수 있는 좋은 기회였다.
필자는 5년 계획 했던 목표를 이루고 마음에 와 닿지 않는 10년 목표를 위해 1년을 허비했다. 말마따나 목표를 잘못 잡았으니 목표를 위해 1년동안 움직이지도 않았다(솔직히 정신없기는 했지만 핑계 ㅅㄱ링). 이 책을 보면서 필자는 목표를 다시 잡고 세부 계획을 좀 더 정리하고자 한다. 솔직히 고맙다. 몇개 빠진 나사들을 다시 줍는 시간이었다.
비전공자가 멘토도 정보도 없는 상태에서 갈피를 잡을 때 읽으면 좋다고 생각한다. 개발자가 되기 위해 수많은 행위들을 추상적으로 잘 설명한다. 추상적? 왜 추상적이라고 표현했을까? 해당 책의 저자는 방향성 까지만 제공하고 있기 때문이다. 최소한의 필요 정보와 방향성만 제공했으면 충분하다. 독자는 책에서 가이드한 정보를 구체화하고 구체화한 것을 토대로 개발자 준비를 하면된다. 막연하게 개발자가 되고 싶다면 해당 서적을 읽어보는 것을 추천한다(개발자의 처절한 현실도 나름 얘기해준다;; ㅋㅋ?).
해당 서적은 포트폴리오 강의를 준비하면서 전지적 취준/비전공자 입장을 헤아리기 위해 선택한 수단 중 한개이다(다른 하나는 비전공자 신입, 실무자 인터뷰). 솔직히 많이 도움이 됐다. 비전공자 입장에서는 진짜 아무 갈피가 없고 시야가 정말 좁을 수 밖에 없겠다는 생각을 많이 했다. 비전공자를 폄하하는게 아니라 현실적으로 제한된 정보를 토대로 개발자가 되기 위해 노력을 해야한다는 전제 자체가 쉽지는 않아보였다. 그래도 이러한 서적 하나하나가 독자들한테 많은 힘이 되는 것 같다.
개발자 입장에서 간접적으로 모델링을 경험할 수 있었다. "현재 사용하고 있는 제품의 DB 모델링은 충분한가?", "과거에 했던 모델링의 필요 개선점?", "개선 했을 때 어떤 점을 더 고려하면 좋을까"와 같은 고민으로 해당 서적을 읽었다. 추상적으로만 인지하고 있던 다음의 사항을 명시적으로 이해할 수 있는 기회가 있었다.
주체/대상/행위: 사전에 모델링할 때 인터뷰를 하거나 분석하는 행위들을 객관적으로 얘기를 못 했습니다. 필자가 하는 행위는 모델링할 때 업무 시스템의 "주체/대상/행위"를 추출하는 행위였다.
ER 서브타입: 보통의 ER 서브타입을 객체 지향의 Sub/Super로 대강 이해했지만 통합, 분리, 혼합이라는 것을 알게 되었다. 이 부분은 더 공부가 필요하다.
어카운트 개념: 마스터 데이터, 행위 데이터를 동일한 성격으로 묶는 행위이다. 모델링에 어카운트가 있으면 모델링의 유연성을 제공할 수 있고 업무에서는 유연한 엔티티로 한층 더 유연하게 문제를 해결할 수 있다. 우리가 데이터 모델링할 때 공통적인 것은 묶고 엔티티 간의 연관관계를 맺는다. 연관관계를 연결할 때 징검다리가 필요할 때가 있다. 이때 어카운트가 징검다리 역할을 해준다.
KDF(Key Derivation Function)는 에플리케이션/프로토콜 서브 로직 등에서 많이 활용할 수 있다.
사용자 정의 보안 프로토콜에서 Key/IV/Nonce 등의 Secret 값을 만들기 위해 사용한다.
우리가 사용하는 인증서의 비밀번호는 PBKDF를 사용한다(실질적으로 표현하면 인증서에 상응하는 PrivateKey를 암호화하는 키를 생성하기 위한 비밀번호).
PBKDF는 패스워드/Hash 기반으로 키를 유도한다.
HKDF는 HMAC을 기반으로 키를 유도한다.
HKDF는 "extract-then-expand"의 페러다임(2개의 모듈)을 갖고 있다.
extract에서는 일반 입력 값으로(엔트로피가 낮은 값으로) 고정 길이의 PRK(의사난수 키) 값을 추출한다.
expand에서는 extract에서 발췌한 PRK를 확장한다.
플로우
HKDF-Extract(salt, IKM) -> PRK(의사난수키)
salt: HMAC에서 Key로 사용(공개 값 사용 가능)
IKM: HMAC 대상 data
salt가 null인 경우 아래와 같음
byte[] salt = DatatypeConverter.parseHexBinary("0000000000000000000000000000000000000000000000000000000000000000");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(salt, "RawBytes"));
byte[] extract = mac.doFinal(IKM);
HKDF-Expand(PRK, info, L) -> OKM(최종 결과)
PRK: Extract의 결과
info: salt와 비슷한 아무 값(생략 가능)
L: 결과 값의 길이
/*
The output OKM is calculated as follows:
N = ceil(L/HashLen)
T = T(1) | T(2) | T(3) | ... | T(N)
OKM = first L bytes of T
where:
T(0) = empty string (zero length)
T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
...
*/
위의 식으로 HMAC 결과 값 만큼 append하여 결과를 출력한다.
만약 HmacSHA256을 사용하고, 원하는 출력값이 96인 경우 위의 where절을 3번 실행한다.
기타 정보
Extract의 salt는 재사용이 가능하며, 없어도 되지만 보안상 추가하는 것이 좋다고 봄.
Expand의 "info"는 여러개의 세션 또는 컨텍스트 간에 중복 결과를 방지한다. IKM은 같은 경우가 많으니 info 값을 salt 처럼 활용하는 것 같다.
만약 extarct에서 사용하는 입력 값이 높은 엔트로피를 갖고있으면 extract는 생략할 수 있다.
최종 결과 값 OKM 값이 해시 길이(예:32byte)보다 낮은 경우 PRK를 대신사용할 수 있지만, 권고하지 않음
본 시리즈는 웹페이지 앞단에서 게시판 페이징을 하는 방법을 소개한다. 즉, JavaScript에서 어떻게 페이징 처리하는지 확인할 수 있다. 피곤하지 않으면 2장 라이브러리 최적화 작업을 진행할 예정이다. 요즘 바빠서 너무 피곤하다...
Pure-JavaSciprt 게시판 페이징 - 1(기본/응용)
Pure-JavaSciprt 게시판 페이징 - 2(라이브러리 최적화)
개요
[들어가면서...]
웹 페이지에서 제일 많이 사용하는 기능은 단연 게시판이다. 그리고 신입 개발자가 처음에 직면하는 벽이 게시판 페이징 작업이다. 게시판 페이징 작업을 구글링 하여 구현하는 거는 진짜 쉽지 않을 것이다. 게시판 페이징 개발 스타일이 다양해서 이것저것 참고하다가 실패할 것이다. 차라리 책에서 제공하는 게시판 페이징 코드를 활용하는 게 정신 건강에 좋을 것이다.
[게시판 페이징 개발 스타일]
그렇다면 어떻게 개발 스타일이 다를까? 다음과 같다.
Java(Spring) / JSP(JSTL) 백단 게시판 페이징
JS 앞단 게시판 페이징
백단 게시판 페이징 스타일은 옛날 스타일이다. 백단에서 게시판 페이징에 필요한 데이터를 가공하여 앞단에 전달, JSP영역에서 JSTL 반복문을 사용하여 게시판 페이징을 구현한다. 단점은 1) 페이지 이동할 때마다 페이지 깜박임, 2) JSP에 종속적인 개발 방법이다. 앞단 게시판 페이징은 REST API가 많이 부각되면서 생기지 않았나 생각한다. 백단 기술 스택에 종속적이지 않고 페이지 깜박이는 현상도 없다. 필자 또한 후자를 소개하고자 한다.
[게시판 페이징 라이브러리]
JS 게시판 페이징 라이브러리는 다양하다. 필자는 JS 개발하면서 2~3개 정도 사용했던 기억이 있다. 웹 페이지 특성상 가시적인 요소가 다분하기 때문에 요구사항이 많다. 이와 같은 요구사항을 모두 충족하는 게시판 페이징 라이브러리는 존재하지 않는다. 비약이 심했지만 진짜 찾다 포기했다. 또한 요구사항 충족을 위한 라이브러리 스터디와 분석이 필요하다. 해당 사항으로 인해 작업 속도가 더 느려질 수 있다. 그래서 결국 필자는 별도의 라이브러리를 개발하여 사용하고 있다. 이유는 단순하다. 외주작업을 혼자 하는데 지랄 같은 요구사항 이행하려면 기능 추가가 가능한 라이브러리가 필요하다. 그리고 실무에서 좀 있다 보면 게시판 페이징 라이브러리를 개발하는 것은 많이 어렵지 않을 것이다.
[실무에서 게시판 페이징 디테일]
외주/회사 업무에서 겪은 게시판 페이징 요구사항은 다음과 같다.
체크박스를 이용한 테이블 제어
특정 로우에 이벤트 핸들링
특정 열에 이벤트 핸들링
특정 로우, 열에 이벤트 핸들링
게시판 로우 제어
퍼블리셔와의 템플릿 협업
주로 업무 시스템 개발하는 인원은 이것보다 좀 더 다양한 요구사항을 겪을 것이다. 필자는 다음의 요구사항 때문에 직접 만들기로 결심했다. 실제 개발 결과물이다.
있을 수 있지만, 각 열에 별개의 이벤트 핸들링을 지원하는 게시판 페이징 라이브러리를 찾지 못했다. 1년 전에 개발한 라이브러리이다.. 기억을 되살리고 테스트하여 시리즈를 연재한다.
라이브러리 소개
게시판 페이징을 하기 위한 복잡한 Pure-JS 라이브러리이다. 대단한 기능은 없다. 말마따나 복잡하다. 필자가 봐도 복잡하다. 라이브러리 최적화가 필요하기는 하다. 해당 라이브러리를 사용하기 위한 필수 값이 존재한다. 당연 필수 값은 게시판 페이징에 사용되는 데이터들이다. 데이터들은 백단 API를 이용해서 반환받을 수 있다.
total: 게시판 페이징 데이터의 총 로우 수
limit: 게시판에 뿌려질 데이터의 개수
offset: 게시판 페이지의 위치
datas: 게시판에 뿌려질 데이터
Oracle 외의 RDBMS를 이용하여 게시판 페이징을 했으면 limit, offset을 알 수 있다. RDBMS에서 limit, offset과 같은 의미고 같은 기능이다.
아 참고로 바빠서 넣지 못한 기능이 페이징 넘버링에 대한 이벤트 바인딩이다. Prev, Next, 페이지 넘버에 대한 이벤트 바인딩은 사용자가 직접 추가해야 한다.
(라이브러리 최적화 때 이벤트 바인딩 기능을 추가하면 괜찮을 것 같다.)
소개는 짧게 하고 바로 데모를 진행한다.
데모 1: 게시판 페이징 기본
백문이불여일견 JS 피들을 확인한다. 차근차근 진행하기 위해 이벤트 바인딩은 하지 않았다. JS, HTML 영역을 보면 쉽게 이해할 수 있다.
JS 영역
function renderTableList(tableDivEl, pageDivEl, tlbLibOpt) {
var tblPageEl = new TblPager( tableDivEl, pageDivEl, tlbLibOpt).generateTablePage();
}
왜 변수 네이밍 컨벤션을 본문에 실었는지...?(내용은 괜찮으나 책의 주제와 연관관계가 강해보이지 않음)
별점
4.3/5.0(체계 있는 본문의 구성, 심도가 있음, 추천+)
대상 독자
작문의 질 향상을 원하는 인원(Best)
블로그를 시작하는 주니어 개발자
선행 도서
없음
관련 도서
개발자를 위한 글쓰기 가이드
느낀 점
해당 서적은 오류 문구 정의, 가이드 문서 작성, 장애 보고서 작성 등의 중요 문서 작성 후 검토할 때 많이 참고할 것 같다. 그만큼 본문의 구성은 깊이가 있다. 좋은 문서를 작성할 수 있도록 안내해주는 종합 지침서의 느낌을 받았다. 필자의 표현이 너무 추상적이다. 어떻게 종합 지침을 하였는지 서술하겠다. 해당 서적의 파트 중 하나인 "릴리스 노트 작성법"을 축약하였다. 대강 저자는 아래와 같이 최종 문서를 어떻게 구성하는지 상세하게 설명한다.
프로젝트의 개선/릴리스 내역 정리
내역의 중요도 분류
내역의 주제별 분류
사용자 관점, 문구 수정
문구 요약
문서 틀에 맞게 적재적소 문구 삽입
끝으로 개발자와 글쓰기의 연관관계를 정리한다. 전적으로 필자의 생각이다. 좋은 개발자의 요소 중 하나는 테크니컬 라이팅 능력이다. 주요 골자는 소통이다. "페이퍼를 이용한 소통". 개발자는 명세서, 설계서, 분석서 등의 문서들을 활용하여 기술과 프로젝트 정보를 효율적으로 전달해야 한다. 소통이 왜 필요해요? 소통은 문제 해결을 위한 선행 행위다. 그래서 개발자에게 있어, 문서 작성 능력은 선택 사항이 아닌 필수 사항이다. 필자가 중요하게 생각하는 문서 작성 능력은 다음과 같다.
사용자 관점에 맞는 문서 구조화 능력
쉽게 얘기하면 문서는 객체 지향 개발 방법론의 "객체" 처럼 구조적이었으면 좋겠다. 객체처럼 역할, 책임, 상태, 행위가 있고 이를 통해 상호 협력(소통)을 할 수 있으면 비로소 그것은 좋은 문서가 아닐까 생각한다.
본 게시물은Vault 공식 문서의 내용과 필자의 생각을 정리하였다. Vault는 무엇인지, 어디에 사용하는지, 왜 사용하는지, 어떻게 사용하는지 서술한다. Vault를 처음 접하는 인원은 Vault의 흐름과 골격을 이해할 수 있는 시간을 갖는다. 또한 Vault를 실질적으로 사용해보는 시간을 갖는다. Vault는 Dev 서버 모드를 지원한다. Dev 서버 모드는 사전 설정이 되어있는 데모 서버라고 생각하면 좋다. 사용자는 해당 데모 서버에서 Vault를 학습할 수 있다. 시간이 괜찮으면 Vault HA를 구성한다(시간이 있으면...).
hashicorp사의 Vault(볼트) - Java-Spring With Vault - 6
Vault Docker 환경 구성
[개요]
yum 패키지 설치 기능을 사용하여 Vault 환경을 구성할 수 있다. 이번에는 Docker를 활용하여 Vault 환경을 구성한다. 해당 환경을 구성하기 위해 Docker는 당연히 사용하며, Docker-Compose까지 사용한다. 디렉토리의 구조는 아래와 같다. 환경 구성 후 가동 및 테스트 API만 호출한다.
본 게시물은Vault 공식 문서의 내용과 필자의 생각을 정리하였다. Vault는 무엇인지, 어디에 사용하는지, 왜 사용하는지, 어떻게 사용하는지 서술한다. Vault를 처음 접하는 인원은 Vault의 흐름과 골격을 이해할 수 있는 시간을 갖는다. 또한 Vault를 실질적으로 사용해보는 시간을 갖는다. Vault는 Dev 서버 모드를 지원한다. Dev 서버 모드는 사전 설정이 되어있는 데모 서버라고 생각하면 좋다. 사용자는 해당 데모 서버에서 Vault를 학습할 수 있다. 시간이 괜찮으면 Vault HA를 구성한다(시간이 있으면...).
$vi ./.bash_profile
VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_ADDR
$source ./.bash_profile
// 또는
$ export VAULT_ADDR='http://127.0.0.1:8200'
// 확인
$set | grep VAULT_ADDR
[Vault 서버 가동 및 초기화]
Vault 서버 가동
"-config"에는 config.hcl의 경로를 지정한다.
$vault server -config=/home1/irteamsu/vault/config.hcl &
Vault 서버 초기화 및 Unsealing
Vault 서버 가동 후 최초 1회 초기화 작업을 진행한다. 해당 초기화 작업으로 Vault 설정정보에 따라 SSS 분할키와 Root 토큰을 생성한다. 별도의 설정 정보가 없을 경우 threshold는 (3, 5)이다. 즉 5개의 분할키를 부여하며, 원본을 만들기 위해서는 최소 3개의 분할키가 필요하다. 필자의 경우 프로덕트 환경이 아니기 때문에 아래의 분할키와 Root 토큰을 keyinfo.txt 파일에 보관하였다.
$vault operator init
2021-06-15T16:43:36.202+0900 [INFO] core: pre-seal teardown complete
Unseal Key 1: Iv9H20vmAUMkDaAfyuar2rSWyGnZgvBOhQPl7df4lrib
Unseal Key 2: G7SnjSZPOaZwkMX9rvCyK8ssdoIMAdAYu+oDo1b9uZ+j
Unseal Key 3: jNfpkii4QiJsuIUVwkuIm1jjIz+bzh5oalsfevXGs5Wc
Unseal Key 4: NnruCilytFhgdQ4zCQmKeSrrV4e8Ijws663trVHAamyl
Unseal Key 5: pqb+bP30/wAraxLITsnjza1ibDyqKIcAHTEZkDzf902W
Initial Root Token: s.ZPBA2pFTJEtmlaUfIEHk1Lq6
Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.
Vault does not store the generated master key. Without at least 3 key to
reconstruct the master key, Vault will remain permanently sealed!
It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.
서버 가동, 초기화 작업을 완료하였다. 그러나 Vault는 아직 "Sealed" 상태이다. 위의 분할키는 Vault의 상태를 "Unsealed" 상태로 전이할 수 있다.
위의 작업을 완료하면 Sealed false 값을 확인할 수 있다. Root 토큰으로 로그인한 사용자는 "vault operator seal" 명령을 사용하여 Vault 서버에 Sealing을 할 수 있다. root 사용자의 경우, vault operator seal을 통해 sealing을 다시 할 수 있다. 이제 Vault를 마음껏 사용할 수 있다.
[Vault 가동 스크립트 작성]
Vault를 기동할 때 "Unsealing" 절차는 필수이다. 해당 절차의 편리를 위해 스크립트를 작성한다. 스크립트는 아래와 같다.
start.sh
#!/bin/bash
partOfKey1=$1
partOfKey2=$2
partOfKey3=$3
if [ "$#" != 3 ]
then
echo "argument is not 3"
exit
else
vault server -config=/home1/irteamsu/vault/config.hcl &
sleep 1
echo -ne '\n'
vault operator unseal $1
vault operator unseal $2
vault operator unseal $3
fi
stop.sh
#!/bin/bash
pid=`ps aux | grep 'vault server' | grep -v grep | awk '{print $2}'`
if [ -z "$pid" ]
then
echo "server is not running"
else
echo "shutdown..."
kill -9 $pid
fi
추가 분기를 사용하여 kill -9, -15 옵션을 결정할 수 있다. 필자는 그냥 "-9"를 사용하였다. 프로덕트 환경의 경우, "-15" 옵션을 위한 분기 사용을 권한다.
$curl \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request PUT \
--data '{"policy":"# Dev servers have version 2 of KV secrets engine mounted by default, so will\n# need these paths to grant permissions:\npath \"secret/data/*\" {\n capabilities = [\"create\", \"update\"]\n}\n\npath \"secret/data/foo\" {\n capabilities = [\"read\"]\n}\n"}' \
-D - http://127.0.0.1:8200/v1/sys/policies/acl/my-policy
이렇게 Dev 모드가 아닌 실제 서버를 설정하였다. 해당 서버는 vault 커맨드를 사용하여 제어할 수 있으며, REST API를 사용하여 제어할 수 있다. 본 게시글은 curl 명령과 함께 REST API를 사용하였다. 실질적으로 프로덕션 레벨에서는 클라이언트 토큰을 생성 후, Secret을 제어하는 것이다. 해당 사항은 Java/Spring환경을 구성한 후 테스트 예정이다.