개발자 스타일마다 다를 순 있지만 개인적으로는 하드코딩을 피하기 위해 정말 노력하는 편이다.
데이터가 변경될 가능성 + 추후 유지보수 + 확장 가능성 등등을 고려하며 추후 데이터의 변경이 있을 때에도 간단하게 변경할 수 있는 구조를 생각하며 구현하는 편인데 처음엔 조금 공수가 드는 것 처럼 보여도 결국 조금만 지나도 큰 효용을 느낄 수 있다고 생각하기 때문이다.
문제
서비스 릴리즈를 앞두고 있던 때에 다양한 이용약관이 추가되어야 하는 상황이었는데
이용약관들은 법적 검토 등의 과정을 통해 다소 여러가지 형태의 각기 다른 레이아웃을 가진 채 노션 (notion)에 정의되어 있었고,
약관을 보여주는 공통 모달 컴포넌트 디자인은 타이틀, 소제목, 본문, bullet, numbering, 여백 등등이 따로 커스텀 되어있는 디자인이었다. 이게 또 약관마다 레이아웃이 다른데 디자인에서는 통일된 부분도 있고 없는 부분도 있는 그런 상황이었다.
논의 초기에는 서버에서 html 을 string으로 변환하여 약관을 정적 데이터로 받을 수 있도록 하려고 했었는데 이런 여러 복잡한 조건들이 있어 백엔드에서 가공된 데이터를 보내주기 힘들 것이라 판단이 들었고, 백엔드 선배님께서 클라이언트 하드코딩으로 넣어야 할 것 같다고 말씀해주셨다.
하지만 매번 약관이 추가되고 변경될 때마다 1만 자가 넘는 약관들을 죄다 넣고 빼고 수정하고 하는 건 현실적으로 좋은 방안도 아닐 뿐더러 그 약관들을 클린하게 관리하거나 유지 보수할 자신도 없었기 때문에 이런 저런 방안을 생각해 보았다.
고려 사항
1. 약관은 모두 약관용 모달 컴포넌트에서 공통적으로 등장해야 한다
> 기본적으로 정해진 약관의 타이틀, Bullets, numberling, list, 조항 타이틀, 본문 등의 스타일이 있었고 해당하는 경우 그에 맞는 스타일로 보여주어야 했었다.
2. 약관은 추후 추가, 변경 및 삭제될 여지가 많다 (서비스 확장 또는 변경 등)
> 확장성을 고려하고, 유지 보수가 간단해야 한다. (스크립트를 실행해서 간단하게 관리하고 싶었다)
방안
그렇게 찾은 방안은 notion 에 정의된 약관을 export 하여 사용하는 방법을 먼저 떠올렸다.
디자인에 정의된 부분들에 대해 따로 식별하여 스타일을 줄 수 있도록 노션 약관 문서에 타이틀은 heading1 / 조항 부분은 heading3 등의 요구 사항들을 정리한 후 요청을 드리며 약관 노션 레이아웃 형식을 정의했다.
처음에는 마크다운으로 추출하여 html로 이를 변환하고 변환한 html string 데이터를 ts 파일로 만들어서 컴포넌트에서 상수를 임포트할 수 있도록 구현했다.
구현하고 나니 문제가 한 가지 있었는데, markdown 을 html 로 변환할 때 사용하는 툴에 따라 html 태그 요소가 달라질 수 있었는데 그러다 보니 실제로는 같은 스타일이 적용되어야 하지만 요소가 달라져서 다시 하나하나 요소를 파악해야 한다는 문제가 있었다.
그렇다면 그냥 노션에서 html 파일을 익스포트해서 사용하는 게 낫겠다 생각했고 html 파일을 익스포트 하고, 불필요한 스타일과 태그들을 지운 뒤 스크립트 태그를 실행하여 ts 파일을 만들어 내도록 했고,
그 후 모든 약관을 띄워줄 수 있는 모달 컴포넌트를 만들어 그 곳에 html 데이터를 파싱해서 보여줄 수 있도록 했다.
결과물
구현 코드
htmlToTs.js
const fs = require('fs');
const path = require('path');
const CONVERT_DIR_PATH = path.join(__dirname, 'src', 'converter');
const INPUT_DIR_PATH = path.join(CONVERT_DIR_PATH, 'html');
const OUTPUT_DIR_PATH = path.join(CONVERT_DIR_PATH, 'ts');
const convertHtmlToTs = (inputPath, outputPath) => {
const htmlFileList = fs.readdirSync(inputPath);
htmlFileList.forEach(fileName => {
const html = fs.readFileSync(path.join(inputPath, fileName), {
encoding: 'utf-8',
});
const outputFileName = fileName.replace('.html', '.ts');
const tsConstantsName = fileName.replace('.html', '').toUpperCase();
const tsCode = `export const ${tsConstantsName} = ${JSON.stringify(html)};`;
fs.writeFileSync(path.join(outputPath, outputFileName), tsCode, {
encoding: 'utf-8',
});
});
};
convertHtmlToTs(INPUT_DIR_PATH, OUTPUT_DIR_PATH);
converter/html 폴더에 있는 파일들을 읽어 온 뒤, html string 값이 상수로 선언되어있는 ts 파일로 변환해주는 node 코드를 작성했다.
그리고 package.json에 아래 스크립트 코드를 추가 해, 스크립트 실행 시 파일 변환이 일어날 수 있도록 했다.
짜잔 그럼 이렇게 상수로 변환된 html string 값을 확인할 수 있다.
이제 약관에 수정사항이 생긴다면 노션 문서를 html로 익스포트 하여 파일을 추가한 뒤 스크립트 실행을 하면 된다.
그럼 이제 이 약관들을 어떻게 관리하며 보여줄 지 고민하다가 아래와 같은 형식으로 구현했다.
terms
...
import {SERVICE_TERMS} from './../converter/ts/service_terms';
export enum TermsType {
...,
SERVICE_TERMS = 'service-terms',
AGE_CONFIRMATION_14UP = 'age-confirmation-14up',
}
export const TERMS = new Map([
...,
[TermsType.SERVICE_TERMS, SERVICE_TERMS],
[TermsType.AGE_CONFIRMATION_14UP, null],
]);
termsType을 enum 문자열 상수로 선언해서 관리하고 실제 모달 컴포넌트에서는 이 값을 통해 약관에 들어갈 html 컨텐츠를 가져 올 수 있도록 설계했다.
Modal.tsx
...
import {TERMS, TermsType} from ...;
import parse from 'html-react-parser';
const Modal = ({type} : {type : TermsType | undefined}) => {
...
const html = useMemo(() => {
if (!type) return null;
return TERMS.get(type as TermsType) ?? null;
}, [type]);
if (!type || !html) return null;
return (
<div >
{parse(html)}
</div>
);
};
약관 모달 타입을 통해 html 파일을 가져오고 그 html 파일을 html-react-parser 라이브러리를 통해 JSX.Element 로 파싱한 html을 보여주고, 세부 스타일 등을 한 컴포넌트에서 공통으로 관리할 수 있게 만들었다.
참고 : https://stackoverflow.com/questions/39758136/how-to-render-html-string-as-real-html
How to render HTML string as real HTML?
Here's what I tried and how it goes wrong. This works: <div dangerouslySetInnerHTML={{ __html: "<h1>Hi there!</h1>" }} /> This doesn't: <div dangerouslySetInnerHTML={{ __ht...
stackoverflow.com
'Code > React - Node' 카테고리의 다른 글
[React] 웹뷰 에서 브릿지 통신으로 IOS/Android 위치 권한 받기 (kotlin / swift) (0) | 2023.10.01 |
---|---|
[React] AF 플러그인으로 애니메이션 간단 구현 (lottie / bodymovin / adobe effects) (1) | 2023.09.17 |
[React/node] 한글 욕설 필터링 기능 구현하기 (node csv 파일 변환) (0) | 2023.09.09 |
[React] svg 에셋을 컴포넌트로 변환하여 사용하기 - SVGR (0) | 2023.09.07 |
[React / Typescript] IOS safari 상태바 색상 변경 / 커스텀 훅을 이용한 동적 처리 (0) | 2022.12.09 |