[sprinter] 미션 1 삽질 기록
[NB] 웹개발 시작하기
총 소요 시간 나흘(도합 56시간)
작업 bgm starcraft live concert - terran one
참고 api 'https://panda-market-api-crud.vercel.app/docs/#/'
git repository missions/1
최종 디렉토리(요약)
mission-1
├ src
├ article
├ Article.mjs
├ ArticleEnums.mjs
├ CreateArticleRequest.mjs
├ CreateArticleResponse.mjs
├ ...
├ product
├ Product.mjs
├ ElectronicProduct.mjs
├ ProductEnums.mjs
├ CreateProductRequest.mjs
├ CreateProductResponse.mjs
├ ...
├ util
├ SprintUtility.mjs
├ ArticleService.js
├ ProductService.js
├ main.js
├ paper.js
놀라지 마시라. 이게 요약본이다.
git repository 에 들어가면 전체 코드를 볼 수 있는데, 상당히 많은 파일들이 있다. 대부분은 미션지에서 하라고 지시된게 아니라, 그냥 내가 진행하다가 필요에 의해서 만든 경우가 대부분이다.
미션은 그렇게 길지 않은데?
맞다.
paper.md 에 적힌 것처럼 미션에 있는 부분만 하면, 사실 그렇게 많은 분량이 아니다. 실제로도 미션을 받은 첫날 필수 요구사항은 다 완료했다.
하지만 미션 진행에 제공된 기간은 약 일주일(휴일 포함) 이었던 점과, 평가 방식이 코드 리뷰라는 점이 내가 좀 더 자유롭게 이 미션을 학습에 활용하기로 결정하게 된 이유가 된다.
특히 코딩 리뷰 라는 점은 권장하는 형태의 결과물은 있을테지만, 꼭 그 안에 갇혀있어야 한다는 뜻은 아닐 것이다.
물론, 미션지에서 명확히 이 부분은 이렇게 하라고 지시된 부분은 따라야하겠지만.
중점적으로 학습한 부분
그래서 미션지를 두고 한 시간 정도 다시 한번 느긋하게 분석하는 시간을 가졌다. 이미 미션에서 요구한 사항은 다 완료했기 때문에 그럴 수 있었던 것이겠지만.
api 문서 최대한 녹여넣기
미션지에서 제공한 **api 문서**를 최대한 녹여넣기 해보기로 했다.
-
필수로 들어가야 항목이나 해당 항목들의 scheme 조건 등을 다 고려해서 정리
-
클래스를 적을 때 미리 기능 함수들을 만들어서 껍데기를 만들기
-
실제 로직 작성
이 순서대로 진행하면서 코드를 다시 처음부터 작성했다. 이때만 해도 하루면 끝나겠지? 라 했지만.. 역시나 아니었다.
리팩토링
특정 기능을 구현해서 동작을 잘하는 지 테스트를 하면, 다시 재활용하기에 불편한 부분들이 보여서 리팩토링을 주기적으로 진행했던 부분이 시간을 많이 잡아먹었다.
처음부터 이렇게 재사용이 가능하도록 다 쪼개놓고 작성했으면 훨씬 더 빨리 끝났을 텐데!
갑작스럽긴 하지만, 나는 flutter 의 freezed 를 아주 많이 좋아한다. 일단 요청할 때랑 응답할 때 뭔가 깔끔 해지는 게 가장 취향에 맞는다.
그래서 이번에 그런 느낌(?)으로 리팩토링 방향성을 잡고 진행했다.
그리고 미션에서 제공한 api 문서를 곰곰히 읽어보니 예외처리 해야 하는 부분이 많아서, 이 구조가 크게 도움 되었다.
꼼꼼히 예외처리 하기(내 기준)
api 문서 를 보면서 예외처리를 많이 신경 써서 했다.
그밖의 가능성도 생각해서 가능한 온갖 쓰레기 값을 넣어보면서 처리를 했다. 물론, 내 기준으로 예외처리를 한 것이기 때문에 차후에 코드 리뷰를 받으면서 질문해볼 예정이다.
이 과정에서 제공된 api 문서에 약간 모순이 있어서 시간이 훨씬 많이 들었다.
분명 scheme 상세 에서는 반드시 하나 이상의 값이 들어가야 한다고 기재되어 있는 scheme 들을 그거에 맞춰서 만들었더니 에러가 엄청나게 많이 떴다.
나는 그냥 단순히 서버에서 GET 해서 받아오는 것 뿐인데 말이다.
나중에 [api try out] 을 했더니 이 영역을 비워서 서버에 올려도 올라가지는게 아닌가! 그러니 다른 사람들이 테스트할 때, 비워서 보내는 경우가 많았다.
여기서 당황해서 한참을 해맸다.
내가 생각했던 특별한 예외 상황은 모종의 이유로 서버에서 직접 해당 데이터를 수정하는 경우만 있을 것이라 생각했었다.
근데 api 문서와 다르게 동작해도 되는 거라니..?
그래서 다시 열심히 해당 경우가 있을 수도 있다고 생각해서 일일이 저 특수 경우들을 찾아서 빈 쓰레기 값을 넣는 걸로 수정했다가..
아무리 생각해도 이건 아닌거 같다 싶어서 다시 api 문서의 scheme 상세에 철저히 맞추도록 수정했다.
누군가 서버에 잘못 올릴 수도 있지만, 어찌되던 간에 나는 api 문서에 적힌 대로 error 를 표시하는 것이 맞다고 생각했다.
이론적으로는 '아니, 사진이나 태그 안 넣어서 글을 올릴 수도 있지!' 할 수 있지만, api 문서는 이렇게 보내라고 지키기 위해 만든 문서니까 최대한 지키는 것이 맞다.
api 문서에 모순이 있는게 교육의 일환으로 있는 건지, 주최 쪽의 실수 인 건지는 모르겠지만 말이다..
새로이 학습한 부분
코드 전체를 정리하는 것은 크게 의미가 없고, 이번 미션을 진행하면서 새로이 학습한 부분만 정리한다.
node.js 내장 기능 path, fs 사용
단순히 학습하기 위해서인데, 매번 새로운 미션을 위해서 새로 node.js 프로젝트를 파는 건 너무 아깝다는 생각이 들었다.
무엇보다도 귀찮은 건 덤.
그래서 node.js 에 있는 path 와 fs 를 사용해 봤다.
import path from 'path';
import fs from 'fs/promises';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const MISSION_ROOT = `./missions/`;
const MISSION_MAIN = `main.js`;
const args = process.argv.slice(2);
const missionNumber = args[0];
if (!missionNumber) {
console.error('미션 번호를 입력하세요. 예: `node index.js 1`');
process.exit(1);
}
const missionPath = path.resolve(__dirname, `${MISSION_ROOT}${missionNumber}/${MISSION_MAIN}`);
try {
await fs.access(missionPath);
const missionModule = await import(missionPath);
if (typeof missionModule.default === 'function') {
missionModule.default();
}
} catch (error) {
if (error.code === 'ENOENT') {
console.error(`[ERROR] 미션 ${missionNumber}의 파일이 존재하지 않습니다: ${missionPath}`);
} else {
console.error(`[ERROR] 미션 ${missionNumber} 실행 중 에러 발생:`, error);
}
process.exit(1);
}
사람 생각은 다 비슷비슷한 지, 찾아보니 비슷한 코드들이 많았다.
그러나 실제로 미션에 제출할 때엔 지정된 reopository 를 새로이 파서 제출해야 하기 때문에, 이 repository 는 최종 제출 전에 내가 시행착오하는 repository 로 쓸 예정이다.
typeof null 은 object 다
const type = typeof null
console.log(type) // Object
금방 찾았던 버그(?) 긴 하지만, javascript 에서 null 을 typeof 로 자료형을 확인해보면 object 로 표시된다.
위에서 말했던, api 문서와 다른 있어서는 안 될 케이스를 예외처리하다가 발견했다.
GET 으로 받을 때 null 일 경우를 찾는데, 걸러도 안 나오는게 아닌가! 그래서 찾아보니 javascript 에서 null 은 object 로 뜨는 게 정상이라고 한다.
예전부터 있었던 버그지만, 하위 호환성을 위해서 수정할 수가 없다고.(?!)
throw new Error 와 console.error
새로이 알게 되었다기 보단, 실제로 써보니 어떨 때 둘을 구분해서 사용해야하는지 대충 감이 왔다.
async function example() {
if (true) {
console.error(`안 멈춰!`)
throw new Error(`멈춰!`)
}
}
원래 단순히 에러 출력만 하면 되니까~ 라는 생각으로 console.error() 를 사용해서 에러 출력을 했었다.
근데 워낙 에러가 많이 뜨니까 너무 짜증나서, 적어도 나만이라도 scheme 조건을 만족하지 못하면 POST 나 PATCH 를 못하도록 죄다 throw new Error() 로 변경했다.
내가 만든 함수를 가져다가 사용하는 사람들은 scheme 상세를 지키지 않으면, 절대로 다음 단계로 넘어갈 수 없다.
분해 할당
이번에 리팩토링하면서 진짜 쏠쏠하게 써먹으면서 손으로 외워버린 분해 할당!
function exmpale(id = '', schemes={}) {
const { title, description } = schemes
}
아, 이 분해 할당이 없었으면, 함수마다 코드가 적어도 5줄씩은 더 길어졌을 지도 모른다.
github 랑 redit 을 뒤적여보는데, 누군가 저렇게 사용한 걸 보고 LMS 에서 배웠던 게 생각났다. 이번 기회에 손에 익을 때까지 주구장창 쓰자고 생각했는데, 실제로 써보니 효율이 엄청 상승했다!
특히, api 호출을 하다보면, 필수값, 선택값이 같이 혼용되어서 보내는 경우가 많다.
필수로 들어가는 값들은 객체가 아닌 일반 자료형과 기본값을 넣어서, 함수를 쓰려는 사람에게 필수값이 어떤 자료형이 들어가는 지 알 수 있게 하고 이 값이 없으면 무조건 에러가 나도록 할 수 있다.
그 이후에 나오는 선택값이 없어도 잘 동작한다!
그밖에 자잘한 배운(?) 점들
- 파일 이름은 가능한 kebab-case 를 많이 쓴다고 한다.
물론 미션지에서는 PascalCase 였기 때문에 나는 PascalCase에 맞췄다.
왜냐면 이건 회사 마다, 프로젝트 마다 정해진 규칙을 따라야 하기 때문이다.
근데 PascalCase 도 모든 코드가 동일하게 따르면서 모이니까 예쁘다!
- 온라인 부트캠프의 특징(?)
사실 배움이라기 보는 온라인 부트캠프의 특징(?)을 느꼈다.
오프라인에서 사람들과 북닥북닥 하는 것과 달리, 온라인 환경에서 부트캠프는 개인이 커뮤니케이션을 하려고 하는 노력을 많이 쏟아야 한다.
나 역시 낯을 많이 가리는 편이지만, 이리저리 일하면서 배운 것들이 있다. 그 중 하나가 형식적(?)이나마 동료들과 스몰 토크하려고 신경쓰는 편이다.
그래서 나름대로 팀원들하고 스몰 토크(?) 해보려고 노력하지만 잘 안된다.
이제 서로 만난(?) 지 일주일 밖에 안 지났으니 당연한거려나..?
부족하다 느낀 점
나흘동안 정신없이 작업하면서 느꼈던 것은 일단 설계 능력은 결국 프로젝트를 많이 하고, 리팩토링을 많이 해보는 것 밖에 없다는 생각이 들었다.
이번 리팩토링을 하면서 배웠던 것들, 특히 분해 할당을 예시로 들면 이제 앞으로 클래스를 만들 때 필요에 따라서 바로 사용해서 생산성을 높일 수 있게 되었기 때문이다.
기획서(미션지)를 읽고 뭐부터 해야할 지 정하기
최대한 빨리 끝내고 다른 스프린터를 돕거나 개인 미니 프로젝트 하면서 연습해야지~ 라 생각했었다.
눈에 보이는 문항 부터 빠르게 완료하다가, 나중에 뒤에서 디렉토리 구조를 수정하거나 코드를 수정해야하는 부분들이 나왔다.
먼저 찬찬히 문서를 훑어보았다면, 하지 않아도 되었을 품이 추가로 들었다.
물론, 주최 쪽에서 학습의 목적에 맞게 의도적으로 리팩토링을 경험해보라고 순서를 설계해둔 느낌이 강했다.
그래도 지나고 나면 이런 품이 더 든게 아쉽다.
나흘만에 끝난 프로젝트가 사흘만에 끝날 수도 있었던 거니까.
git commit 메세지 적는 법
기능 하나 만들고, commit 하고 해야지~ 라고 생각은 했다.
다만, 하다보니 삘 받아서 달리다가 정신 차리면 해뜨고 정신차리면 어억? commit 해야 하는데 한게 너무 많네..? 가 되었을 뿐.
이게 리팩토링을 하면서 클래스 하나의 양이 확 줄어들고 가독성이 좋아지는 걸 보면 도파민이 엄청 나온다.
거기다가 한 클래스를 리팩토링 하기 위해서 기능들을 쪼개놓으면, 어, 다른 클래스도 이제 이 기능들만 쓰면 금방(?) 되는데.. 하다가 적절한 commit 타이밍을 놓치게 된 것이다.
그래서 연휴 기간 동안 미니 프로젝트를 하는 동안은, 한 기능 만들고 커밋 하는 걸 꼭 지켜서 해볼 생각이다.