2021-01-11
오늘은 브랜드 크롤링 프로젝트에서 버그가 생기는 크롤러를 수정하고, 새로운 브랜드 크롤러를 추가하는 일을 했다. 새로 알게된 지식은 ld+json이고, cto님이 좋은 코드를 짜기 위해 공부하는 방법, 좋은 코드는 무엇일지 짧게 설명 해주신점이 도움이 되었다.
1️⃣ ld+json이라는 개념과 구조화된 데이터의 존재에 대해 처음 알게되었다.
Googel 검색 엔진 같은 경우 어떠한 페이지의 콘텐츠를 자동으로 딱 파악해야할 필요성이 있는데, 이 때 페이지에 구조화된 데이터를 포함하면 확실한 단서를 제공하여 내용을 파악하는데 도움이 된다. 예를들어, 상품인 경우 type이 Product, 영화인 경우 type이 movie라던지, 레시피인 경우는 아래와 같은 구조를 띈다. 구조화된 데이터는 페이지에 관한 정보를 제공하고 페이지 콘텐츠를 분류하기 위한 표준화된 방식으로 정보를 제공한다.
Google 검색은 JSON-LD, 마이크로데이터, RDFa 형식의 구조화된 데이터를 지원한다. 그 중 google이 가장 권장하는 JSON-LD는 페이지 헤드 또는 본문의 <script>
태그 내에 삽입되는 자바스크립트 표기이다.
ex) 레시피 제목, 레시피 작성자 및 기타 세부정보를 설명하는 구조화된 JSON-LD 데이터 스니펫
(스니펫(snippet)은 재사용 가능한 소스 코드, 기계어, 텍스트의 작은 부분을 일컫는 프로그래밍 용어이다.)
원래는 디폴트로는 meta[property="og:title"]
와 같이 body보다 변동될 가능성이 상당히 적은 meta태그 중심으로 크롤링을 했다. 하지만, 이 중 원하는 정보가 없고, ld+json이 존재하는 경우 여기에서 원하는 정보를 가져오는 것도 좋은방법이라 생각하여 ldJson을 객체로 반환하는 getLdJsonObject()라는 라이브러리 함수를 만들었다.
export const getLdJsonObject = ($: CheerioStatic, index: number = 0): any => {
const scriptEles = $('script[type ="application/ld+json"]')?.toArray();
if (!scriptEles?.length) {
return {};
}
return JSON.parse(scriptEles[index].firstChild.data);
};
cheerio는 jQuery셀렉터로 HTML 문서를 파싱(parsing)하여 필요한 정보만을 가져올 수 있도록 도와주는 모듈이다.(사용방법은 jQuery와 유사하다.) 위 함수에서는 cheerio로 ld+json에 해당하는 script를 가져오고, 찾아낸 ld+json이 여러 개 있을 수도 있기 때문에 원하는 index에 해당하는 script의 데이터를 JSON으로 반환한다.
2️⃣ 좋은코드?
이 함수는 사실 내가 거의 10줄 정도 짠 코드를 cto님이 줄이신거다..^~^
export const getLdJsonObject = ($: CheerioStatic, index: number = 0): any => {
const scriptEles = $('script[type ="application/ld+json"]')?.toArray();
if (!scriptEles?.length) {
return {};
}
return JSON.parse(scriptEles[index].firstChild.data);
};
// 원래 코드
export const getLdJsonObject = ($: CheerioStatic, index: number = 0): any => {
let result = {};
const scriptHtml = $('script[type ="application/ld+json"]');
if (!scriptHtml) return result;
scriptHtml.each((idx, elem) => {
return JSON.parse(scriptEles[index].firstChild.data);
if (index === idx) {
const ldJsonObject = JSON.parse(elem.firstChild.data);
if (ldJsonObject) result = ldJsonObject;
return false;
}
});
return result;
};
이 코드에 대한 리뷰를 받으며 좋은 코드는 어떤건지에 대해 좀 더 알게되었다.
- 고유한 기능을 하는 독립적인 모듈로 분리된다.
- 모듈의 기능을 파악하기 쉽다.
- 불필요한 line이 없다.
- 이후 line에 의존적인 내용을 작성하지 않는다.
--> 코드가 위에서 부터 쭉 막힘없이 읽혀야 좋은 코드- 주석은 정말 필요할 때만 사용한다.
- 언어가 제공하는 built-in 기능들을 잘 활용한다
- 잘 정돈된, 합리적인 컨벤션을 따른다. (Prettier, Eslint 사용)
if(isLoading){
if(data){
data.map(({name, value}) => {
// 중략
})
// 중략
}else{
return;
}
}else{
return;
}
위 같은 경우 맥락이 늘어져, 읽는 사람이 파악하기 어려우므로
(3항연산자((조건)? (참인 경우):(거짓인 경우))는 명확하게 한 줄로 끝나는 경우에만 사용한다.)
// 이렇게 특별한 case, escape case를 위쪽에 배치하는 것이 좋다!
if(!isLoading || !data){
return;
}
data.map(({name, value}) => {
// 중략
})
3️⃣ 핔크롬에서 안긁어와지면 waitUntil 변경하기
2021-01-12
1️⃣ {...question } spread형식으로 넘기면 더 깔끔
2️⃣ 리액트 문서 심화 - context, hoc
(어드민 페이지 개발 시 계속 같은 페이지 반복되는 경우, hoc를 이용해서 코드를 최적화 시킬 수 있다.)
3️⃣ 한글 순 정렬
const brandList: IBrand[] = data?.results.sort(
({ nameKor: nameA }, { nameKor: nameB }) => {
return nameA.localeCompare(nameB);
}
);
referenceStr.localeCompare(compareString[, locales[, options]])
'a'.localeCompare('c'); // return -2 or -1 (와 같은 음수)
'check'.localeCompare('against'); // return 2 or 1 (와 같은 음수)
'a'.localeCompare('a'); // 0
4️⃣ 리액트 hook 공부하기!
참고링크: 네이버 쇼룸:쇼핑 개발자를 부탁해