들어가며
안녕하세요, 초원입니다. 👩🏻💻
새로운 티켓 할당 소식을 전하러 오랜만에 돌아왔어요! 📪
그동안 퍼블리싱이나 데이터 패칭 등의 업무만 수행하다가,
이번에 라이브러리 적용이 가능한지 테스트해보라는 임무를 처음 부여받았는데요.
기분 좋고 설레고 .. 재밌게 작업한 내용을 공유해보려고 합니다. ☝🏻☝🏻 오예!
Express 서버 설정부터, sign-generator 라이브러리를 활용한 전자직인 생성까지의 과정을 자세히 설명할게요.
그럼 시작해볼까요?
GitHub - ketaro01/sign-generator: 도장 및 서명을 svg 형태로 생성하기 위한 라이브러리 입니다.
도장 및 서명을 svg 형태로 생성하기 위한 라이브러리 입니다. Contribute to ketaro01/sign-generator development by creating an account on GitHub.
github.com

먼저 npm으로 필요한 패키지를 설치합니다. 아주 간단하죠?

갑자기 .. 서버요? 간단하지 않아 ..

뭐 일단 필요하다고 하니 시작해봅시다 ..
Express 서버 설정
수많은 에러를 마주하고 완성한 저의 서버 코드입니다! (꺅)
Node.js를 사용하여 Express 서버를 설정하고,
sign-generator 라이브러리를 사용해 사용자 이름으로 전자 직인을 생성하는 예제입니다.
전체코드
// server.js
import express from "express";
import cors from "cors";
import signGenerator from "sign-generator"; // default import
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const { createStamp } = signGenerator; // 객체 디스트럭처링으로 createSign 가져오기
const app = express();
const port = 3001;
app.use(cors());
const fontsPath = path.join(__dirname, "public", "Resources", "Fonts");
app.use("/Resources/Fonts", express.static(fontsPath));
app.get("/api/sign/:text", async (req, res) => {
try {
const fonts = ["NanumGothic-Regular.ttf", "NanumGothic-Bold.ttf", "NanumGothic-ExtraBold.ttf"].map((value) =>
path.join(fontsPath, value),
);
console.log("Fonts:", fonts);
console.log("Text:", req.params.text);
const stampItems = await createStamp(req.params.text, fonts);
const lastStamp = stampItems[stampItems.length - 1]; // 마지막 스탬프만 추출
res.status(200).send(lastStamp);
} catch (e) {
console.error("Error generating sign:", e);
res.status(500).send({ error: "Failed to generate sign", details: e.message });
}
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
1. 모듈 가져오기
import express from "express";
import cors from "cors";
import signGenerator from "sign-generator"; // default import
import path from "path";
import { fileURLToPath } from "url";
• express: 웹 서버 프레임워크입니다.
• cors: Cross-Origin Resource Sharing(CORS)을 허용하는 미들웨어입니다.
• signGenerator: 전자 서명 및 직인 생성을 위한 라이브러리입니다.
• path: 파일 및 디렉터리 경로 조작을 위한 Node.js 기본 모듈입니다.
• fileURLToPath 및 url: 모듈의 파일 경로를 얻기 위한 유틸리티입니다.
마주한 에러들

나중에 소개할 sign-generator 컴포넌트는 default 형식으로 export를 해주는데,
{ } 감쌌다고 화내고 있네요 .. 얼른 떼어줬습니다.
sign-generator 자체를 import하고 아래 3번 과정에서처럼 가져와줬어요.

cors 패키지 없다고 또 화를 내길래 얼른 설치해줬습니다.
npm install cors
또, 라이브러리 설명에는 CommonJS 방식으로 아래와 같이 모듈을 가져오고 있는데요,
const express = require('express');
const { createSign, createStamp } = require('sign-generator');
const path = require('path');
저희는 지금 ES6 모듈 방식을 사용하고 있기 때문에 import로 가져올 수 있도록 수정했답니다! 👏🏻
2. 파일 및 디렉토리 경로 설정
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
• __filename: 현재 모듈 파일의 전체 경로입니다.
• __dirname: 현재 모듈이 위치한 디렉터리의 경로입니다.
3. sign-generator의 createStamp 함수 가져오기
const { createStamp } = signGenerator; // 객체 구조분해할당으로 createSign 가져오기
• createStamp: 주어진 텍스트와 폰트를 사용해 직인(스탬프)을 생성하는 함수입니다.
사인을 만들고 싶으면 createSign을 불러오면 됩니다.
저는 도장처럼 생긴 직인이 필요해서 createStamp를 사용했어요.
1번에서 말씀드렸듯 import 해온 signGenerator에서 구조분해할당 방식으로 꺼내다 쓰고 있죠? 굿!
4. Express 애플리케이션 설정
const app = express();
const port = 3001;
app.use(cors());
• app: Express 애플리케이션 인스턴스입니다.
• port: 서버가 실행될 포트 번호입니다.
• app.use(cors()): CORS를 활성화합니다.
5. 폰트 경로 설정
const fontsPath = path.join(__dirname, "public", "Resources", "Fonts");
• fontsPath: 직인 생성에 사용할 폰트 파일들이 위치한 디렉터리 경로입니다.
public에 바로 폰트 파일들을 넣으셨다면 fontPath = "public" 하셔도 됩니다!
제 프로젝트 구조는 public > Resources > Fonts 안에 파일들이 있어서 ..
6. 정적 파일 서빙
app.use("/Resources/Fonts", express.static(fontsPath));
• /Resources/Fonts 경로로 들어오는 요청에 대해 fontsPath 디렉터리의 정적 파일을 서빙합니다.
7. API 엔드포인트 설정
app.get("/api/sign/:text", async (req, res) => {
try {
const fonts = ["NanumGothic-Regular.ttf", "NanumGothic-Bold.ttf", "NanumGothic-ExtraBold.ttf"].map((value) =>
path.join(fontsPath, value),
);
console.log("Fonts:", fonts);
console.log("Text:", req.params.text);
const stampItems = await createStamp(req.params.text, fonts);
const lastStamp = stampItems[stampItems.length - 1]; // 마지막 스탬프만 추출
res.status(200).send(lastStamp);
} catch (e) {
console.error("Error generating sign:", e);
res.status(500).send({ error: "Failed to generate sign", details: e.message });
}
});
• app.get("/api/sign/:text"): GET 요청을 처리하는 API 엔드포인트입니다. :text는 URL 매개변수로, 사용자 이름을 받습니다.
• try 블록:
• fonts 배열: 직인 생성을 위해 사용할 폰트 파일들의 경로를 설정합니다.
• createStamp: 요청된 텍스트와 폰트를 사용해 직인을 생성합니다.
• const lastStamp = stampItems[stampItems.length - 1]: 생성된 여러 직인 중 마지막 직인을 선택합니다. (9개나 보여줌)
• res.status(200).send(lastStamp): 생성된 직인을 클라이언트에 응답합니다.
• catch 블록:
• 에러 발생 시 콘솔에 출력하고, 500 상태 코드와 함께 에러 메시지를 클라이언트에 응답합니다.
마주한 에러

저희는 woff2 형식의 폰트를 사용 중이었는데 사용하는 라이브러리에서 지원하지 않는다고 ... 합니다.
구글 폰트에서 ttf 형식의 폰트 파일을 저장해서 문제 해결 ..!
Nanum Gothic - Google Fonts
Nanum Gothic is a contemporary sans-serif typeface with a warm touch, and it is expertly hinted for screen use. It is part of the Nanum fonts (나눔글꼴) – a set of
fonts.google.com
8. 서버 시작
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
• 서버를 지정된 포트에서 시작하고, 콘솔에 서버가 실행 중임을 알리는 메시지를 출력합니다.
아참참,
// vite.config.ts
import react from "@vitejs/plugin-react-swc";
import path from "path";
import { defineConfig } from "vite";
import svgr from "vite-plugin-svgr";
export default defineConfig({
server: {
open: true,
host: true,
port: 3000,
proxy: {
"/api": {
...
},
"/storage": {
...
},
"/local-api": { // <-------------- 요거 --------
target: "http://localhost:3001",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/local-api/, "/api"),
},
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
plugins: [react(), svgr()],
});
📌 추가해줬습니당 제가 따로 만든 로컬 서버로 요청가도록 !!
// package.json
"scripts": {
"server": "node server.js",
...
},
📌 명령어도 추가 !!
자, 이제 뭔가 준비가 좀 된 것 같은데요.
이제 서버에 요청하는 코드를 작성해봅시다. 🫨🫨
SignGenerator 컴포넌트 생성
이 컴포넌트는 사용자가 입력한 텍스트를 기반으로 전자 직인(SVG 형식)을 생성하여 화면에 표시합니다.
전체코드
import { useState, useEffect } from "react";
import { ReactSVG } from "react-svg";
interface SignGeneratorProps {
text: string;
}
const fetchSignSvg = async (text: string) => {
const response = await fetch(`/local-api/sign/${encodeURIComponent(text)}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText);
}
return await response.text();
};
function SignGenerator({ text }: SignGeneratorProps) {
const [svgUrl, setSvgUrl] = useState<string>("");
const [error, setError] = useState<string>("");
useEffect(() => {
let isMounted = true;
const generateSign = async () => {
try {
const signSvg = await fetchSignSvg(text);
const blob = new Blob([signSvg], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob);
if (isMounted) {
setSvgUrl(url);
setError("");
}
} catch (err) {
console.error("Error generating sign:", err);
if (isMounted) {
setError("Failed to generate sign. Please try again.");
}
}
};
if (text) {
generateSign();
}
return () => {
isMounted = false;
if (svgUrl) {
URL.revokeObjectURL(svgUrl);
}
};
}, [text, svgUrl]);
return (
<div>
{error && <p>{error}</p>}
{svgUrl && <ReactSVG src={svgUrl} />}
</div>
);
}
export default SignGenerator;
1. 상태 관리
const [svgUrl, setSvgUrl] = useState<string>("");
const [error, setError] = useState<string>("");
• svgUrl: 생성된 전자 직인(SVG 형식)의 URL을 저장하는 상태입니다.
• error: 에러 메시지를 저장하는 상태입니다.
2. 비동기 함수
const fetchSignSvg = async (text: string) => {
const response = await fetch(`/local-api/sign/${encodeURIComponent(text)}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText);
}
return await response.text();
};
• text를 인코딩하여 서버에 요청을 보냅니다.
• 응답이 정상적이지 않으면 에러를 발생시킵니다.
• 정상 응답인 경우, SVG 데이터를 반환합니다.
3. useEffect 훅
useEffect(() => {
let isMounted = true;
const generateSign = async () => {
try {
const signSvg = await fetchSignSvg(text);
const blob = new Blob([signSvg], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob);
if (isMounted) {
setSvgUrl(url);
setError("");
}
} catch (err) {
console.error("Error generating sign:", err);
if (isMounted) {
setError("Failed to generate sign. Please try again.");
}
}
};
if (text) {
generateSign();
}
return () => {
isMounted = false;
if (svgUrl) {
URL.revokeObjectURL(svgUrl);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [text]);
• isMounted 플래그를 사용하여 컴포넌트가 언마운트된 후에도 비동기 작업이 완료되면 상태를 업데이트하지 않도록 합니다.
• generateSign 함수를 정의하여 전자 직인을 생성합니다.
• text가 변경될 때마다 generateSign 함수가 호출됩니다.
• svgUrl이 변경될 때마다 이전 URL을 해제(revoke)하여 메모리 누수를 방지합니다.
자꾸 깜빡거려서 의존성 배열에서 svgURL은 뺐습니다 !! 🤷🏻♀️
4. 컴포넌트 렌더링
return (
<div>
{error && <p>{error}</p>}
{svgUrl && <ReactSVG src={svgUrl} />}
</div>
);
• error 상태가 존재하면 에러 메시지를 표시합니다.
• svgUrl 상태를 사용하여 생성된 전자 직인을 화면에 표시합니다. ReactSVG 컴포넌트를 사용하여 SVG를 안전하게 렌더링합니다.
return (
<div>
{error && <p>{error}</p>}
<div dangerouslySetInnerHTML={{ __html: svg }} />
</div>
);
원래 위와 같이 dangerouslySetInnerHTML 속성을 사용하여 SVG 데이터를 직접 삽입했는데요,
좋지 않은 방식 같아 GPT에게 물어보고 리팩토링을 했답니다 👊🏻
이제 진짜 갖다 쓰기만 하면 됨 ..
전체코드
function Component() {
const [name, setName] = useState("");
const [isSealCreated, setIsSealCreated] = useState(false);
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
};
const handleCreateSeal = () => {
setIsSealCreated(true);
};
return (
<>
<Input value={name} onChange={handleNameChange} />
<Button onClick={handleCreateSeal}>직인 생성하기</Button>
{isSealCreated && <SignGenerator text={name} />}
</>
);
}
export default Component;
뿌듯해 ...................

끝 ......
인줄 알았지만 이름이 두 글자이면 타원형이 된다 .......?
to be continued ......
'슬기로운 회사생활' 카테고리의 다른 글
로컬 개발 환경에서 dev 환경으로 리다이렉트 되는 현상, 환경 변수 설정 실수 피하기 (0) | 2024.07.16 |
---|---|
2차 개발이 시작되고 피그마 디자인이 ... 공통 컴포넌트 제작 어떻게 하지? (0) | 2024.07.16 |
쏟아지는 페이지들, 프론트엔드에서 어떻게 설계하면 좋을까? (0) | 2024.07.09 |
들어가며
안녕하세요, 초원입니다. 👩🏻💻
새로운 티켓 할당 소식을 전하러 오랜만에 돌아왔어요! 📪
그동안 퍼블리싱이나 데이터 패칭 등의 업무만 수행하다가,
이번에 라이브러리 적용이 가능한지 테스트해보라는 임무를 처음 부여받았는데요.
기분 좋고 설레고 .. 재밌게 작업한 내용을 공유해보려고 합니다. ☝🏻☝🏻 오예!
Express 서버 설정부터, sign-generator 라이브러리를 활용한 전자직인 생성까지의 과정을 자세히 설명할게요.
그럼 시작해볼까요?
GitHub - ketaro01/sign-generator: 도장 및 서명을 svg 형태로 생성하기 위한 라이브러리 입니다.
도장 및 서명을 svg 형태로 생성하기 위한 라이브러리 입니다. Contribute to ketaro01/sign-generator development by creating an account on GitHub.
github.com

먼저 npm으로 필요한 패키지를 설치합니다. 아주 간단하죠?

갑자기 .. 서버요? 간단하지 않아 ..

뭐 일단 필요하다고 하니 시작해봅시다 ..
Express 서버 설정
수많은 에러를 마주하고 완성한 저의 서버 코드입니다! (꺅)
Node.js를 사용하여 Express 서버를 설정하고,
sign-generator 라이브러리를 사용해 사용자 이름으로 전자 직인을 생성하는 예제입니다.
전체코드
// server.js
import express from "express";
import cors from "cors";
import signGenerator from "sign-generator"; // default import
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const { createStamp } = signGenerator; // 객체 디스트럭처링으로 createSign 가져오기
const app = express();
const port = 3001;
app.use(cors());
const fontsPath = path.join(__dirname, "public", "Resources", "Fonts");
app.use("/Resources/Fonts", express.static(fontsPath));
app.get("/api/sign/:text", async (req, res) => {
try {
const fonts = ["NanumGothic-Regular.ttf", "NanumGothic-Bold.ttf", "NanumGothic-ExtraBold.ttf"].map((value) =>
path.join(fontsPath, value),
);
console.log("Fonts:", fonts);
console.log("Text:", req.params.text);
const stampItems = await createStamp(req.params.text, fonts);
const lastStamp = stampItems[stampItems.length - 1]; // 마지막 스탬프만 추출
res.status(200).send(lastStamp);
} catch (e) {
console.error("Error generating sign:", e);
res.status(500).send({ error: "Failed to generate sign", details: e.message });
}
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
1. 모듈 가져오기
import express from "express";
import cors from "cors";
import signGenerator from "sign-generator"; // default import
import path from "path";
import { fileURLToPath } from "url";
• express: 웹 서버 프레임워크입니다.
• cors: Cross-Origin Resource Sharing(CORS)을 허용하는 미들웨어입니다.
• signGenerator: 전자 서명 및 직인 생성을 위한 라이브러리입니다.
• path: 파일 및 디렉터리 경로 조작을 위한 Node.js 기본 모듈입니다.
• fileURLToPath 및 url: 모듈의 파일 경로를 얻기 위한 유틸리티입니다.
마주한 에러들

나중에 소개할 sign-generator 컴포넌트는 default 형식으로 export를 해주는데,
{ } 감쌌다고 화내고 있네요 .. 얼른 떼어줬습니다.
sign-generator 자체를 import하고 아래 3번 과정에서처럼 가져와줬어요.

cors 패키지 없다고 또 화를 내길래 얼른 설치해줬습니다.
npm install cors
또, 라이브러리 설명에는 CommonJS 방식으로 아래와 같이 모듈을 가져오고 있는데요,
const express = require('express');
const { createSign, createStamp } = require('sign-generator');
const path = require('path');
저희는 지금 ES6 모듈 방식을 사용하고 있기 때문에 import로 가져올 수 있도록 수정했답니다! 👏🏻
2. 파일 및 디렉토리 경로 설정
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
• __filename: 현재 모듈 파일의 전체 경로입니다.
• __dirname: 현재 모듈이 위치한 디렉터리의 경로입니다.
3. sign-generator의 createStamp 함수 가져오기
const { createStamp } = signGenerator; // 객체 구조분해할당으로 createSign 가져오기
• createStamp: 주어진 텍스트와 폰트를 사용해 직인(스탬프)을 생성하는 함수입니다.
사인을 만들고 싶으면 createSign을 불러오면 됩니다.
저는 도장처럼 생긴 직인이 필요해서 createStamp를 사용했어요.
1번에서 말씀드렸듯 import 해온 signGenerator에서 구조분해할당 방식으로 꺼내다 쓰고 있죠? 굿!
4. Express 애플리케이션 설정
const app = express();
const port = 3001;
app.use(cors());
• app: Express 애플리케이션 인스턴스입니다.
• port: 서버가 실행될 포트 번호입니다.
• app.use(cors()): CORS를 활성화합니다.
5. 폰트 경로 설정
const fontsPath = path.join(__dirname, "public", "Resources", "Fonts");
• fontsPath: 직인 생성에 사용할 폰트 파일들이 위치한 디렉터리 경로입니다.
public에 바로 폰트 파일들을 넣으셨다면 fontPath = "public" 하셔도 됩니다!
제 프로젝트 구조는 public > Resources > Fonts 안에 파일들이 있어서 ..
6. 정적 파일 서빙
app.use("/Resources/Fonts", express.static(fontsPath));
• /Resources/Fonts 경로로 들어오는 요청에 대해 fontsPath 디렉터리의 정적 파일을 서빙합니다.
7. API 엔드포인트 설정
app.get("/api/sign/:text", async (req, res) => {
try {
const fonts = ["NanumGothic-Regular.ttf", "NanumGothic-Bold.ttf", "NanumGothic-ExtraBold.ttf"].map((value) =>
path.join(fontsPath, value),
);
console.log("Fonts:", fonts);
console.log("Text:", req.params.text);
const stampItems = await createStamp(req.params.text, fonts);
const lastStamp = stampItems[stampItems.length - 1]; // 마지막 스탬프만 추출
res.status(200).send(lastStamp);
} catch (e) {
console.error("Error generating sign:", e);
res.status(500).send({ error: "Failed to generate sign", details: e.message });
}
});
• app.get("/api/sign/:text"): GET 요청을 처리하는 API 엔드포인트입니다. :text는 URL 매개변수로, 사용자 이름을 받습니다.
• try 블록:
• fonts 배열: 직인 생성을 위해 사용할 폰트 파일들의 경로를 설정합니다.
• createStamp: 요청된 텍스트와 폰트를 사용해 직인을 생성합니다.
• const lastStamp = stampItems[stampItems.length - 1]: 생성된 여러 직인 중 마지막 직인을 선택합니다. (9개나 보여줌)
• res.status(200).send(lastStamp): 생성된 직인을 클라이언트에 응답합니다.
• catch 블록:
• 에러 발생 시 콘솔에 출력하고, 500 상태 코드와 함께 에러 메시지를 클라이언트에 응답합니다.
마주한 에러

저희는 woff2 형식의 폰트를 사용 중이었는데 사용하는 라이브러리에서 지원하지 않는다고 ... 합니다.
구글 폰트에서 ttf 형식의 폰트 파일을 저장해서 문제 해결 ..!
Nanum Gothic - Google Fonts
Nanum Gothic is a contemporary sans-serif typeface with a warm touch, and it is expertly hinted for screen use. It is part of the Nanum fonts (나눔글꼴) – a set of
fonts.google.com
8. 서버 시작
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
• 서버를 지정된 포트에서 시작하고, 콘솔에 서버가 실행 중임을 알리는 메시지를 출력합니다.
아참참,
// vite.config.ts
import react from "@vitejs/plugin-react-swc";
import path from "path";
import { defineConfig } from "vite";
import svgr from "vite-plugin-svgr";
export default defineConfig({
server: {
open: true,
host: true,
port: 3000,
proxy: {
"/api": {
...
},
"/storage": {
...
},
"/local-api": { // <-------------- 요거 --------
target: "http://localhost:3001",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/local-api/, "/api"),
},
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
plugins: [react(), svgr()],
});
📌 추가해줬습니당 제가 따로 만든 로컬 서버로 요청가도록 !!
// package.json
"scripts": {
"server": "node server.js",
...
},
📌 명령어도 추가 !!
자, 이제 뭔가 준비가 좀 된 것 같은데요.
이제 서버에 요청하는 코드를 작성해봅시다. 🫨🫨
SignGenerator 컴포넌트 생성
이 컴포넌트는 사용자가 입력한 텍스트를 기반으로 전자 직인(SVG 형식)을 생성하여 화면에 표시합니다.
전체코드
import { useState, useEffect } from "react";
import { ReactSVG } from "react-svg";
interface SignGeneratorProps {
text: string;
}
const fetchSignSvg = async (text: string) => {
const response = await fetch(`/local-api/sign/${encodeURIComponent(text)}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText);
}
return await response.text();
};
function SignGenerator({ text }: SignGeneratorProps) {
const [svgUrl, setSvgUrl] = useState<string>("");
const [error, setError] = useState<string>("");
useEffect(() => {
let isMounted = true;
const generateSign = async () => {
try {
const signSvg = await fetchSignSvg(text);
const blob = new Blob([signSvg], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob);
if (isMounted) {
setSvgUrl(url);
setError("");
}
} catch (err) {
console.error("Error generating sign:", err);
if (isMounted) {
setError("Failed to generate sign. Please try again.");
}
}
};
if (text) {
generateSign();
}
return () => {
isMounted = false;
if (svgUrl) {
URL.revokeObjectURL(svgUrl);
}
};
}, [text, svgUrl]);
return (
<div>
{error && <p>{error}</p>}
{svgUrl && <ReactSVG src={svgUrl} />}
</div>
);
}
export default SignGenerator;
1. 상태 관리
const [svgUrl, setSvgUrl] = useState<string>("");
const [error, setError] = useState<string>("");
• svgUrl: 생성된 전자 직인(SVG 형식)의 URL을 저장하는 상태입니다.
• error: 에러 메시지를 저장하는 상태입니다.
2. 비동기 함수
const fetchSignSvg = async (text: string) => {
const response = await fetch(`/local-api/sign/${encodeURIComponent(text)}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText);
}
return await response.text();
};
• text를 인코딩하여 서버에 요청을 보냅니다.
• 응답이 정상적이지 않으면 에러를 발생시킵니다.
• 정상 응답인 경우, SVG 데이터를 반환합니다.
3. useEffect 훅
useEffect(() => {
let isMounted = true;
const generateSign = async () => {
try {
const signSvg = await fetchSignSvg(text);
const blob = new Blob([signSvg], { type: "image/svg+xml" });
const url = URL.createObjectURL(blob);
if (isMounted) {
setSvgUrl(url);
setError("");
}
} catch (err) {
console.error("Error generating sign:", err);
if (isMounted) {
setError("Failed to generate sign. Please try again.");
}
}
};
if (text) {
generateSign();
}
return () => {
isMounted = false;
if (svgUrl) {
URL.revokeObjectURL(svgUrl);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [text]);
• isMounted 플래그를 사용하여 컴포넌트가 언마운트된 후에도 비동기 작업이 완료되면 상태를 업데이트하지 않도록 합니다.
• generateSign 함수를 정의하여 전자 직인을 생성합니다.
• text가 변경될 때마다 generateSign 함수가 호출됩니다.
• svgUrl이 변경될 때마다 이전 URL을 해제(revoke)하여 메모리 누수를 방지합니다.
자꾸 깜빡거려서 의존성 배열에서 svgURL은 뺐습니다 !! 🤷🏻♀️
4. 컴포넌트 렌더링
return (
<div>
{error && <p>{error}</p>}
{svgUrl && <ReactSVG src={svgUrl} />}
</div>
);
• error 상태가 존재하면 에러 메시지를 표시합니다.
• svgUrl 상태를 사용하여 생성된 전자 직인을 화면에 표시합니다. ReactSVG 컴포넌트를 사용하여 SVG를 안전하게 렌더링합니다.
return (
<div>
{error && <p>{error}</p>}
<div dangerouslySetInnerHTML={{ __html: svg }} />
</div>
);
원래 위와 같이 dangerouslySetInnerHTML 속성을 사용하여 SVG 데이터를 직접 삽입했는데요,
좋지 않은 방식 같아 GPT에게 물어보고 리팩토링을 했답니다 👊🏻
이제 진짜 갖다 쓰기만 하면 됨 ..
전체코드
function Component() {
const [name, setName] = useState("");
const [isSealCreated, setIsSealCreated] = useState(false);
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
};
const handleCreateSeal = () => {
setIsSealCreated(true);
};
return (
<>
<Input value={name} onChange={handleNameChange} />
<Button onClick={handleCreateSeal}>직인 생성하기</Button>
{isSealCreated && <SignGenerator text={name} />}
</>
);
}
export default Component;
뿌듯해 ...................

끝 ......
인줄 알았지만 이름이 두 글자이면 타원형이 된다 .......?
to be continued ......
'슬기로운 회사생활' 카테고리의 다른 글
로컬 개발 환경에서 dev 환경으로 리다이렉트 되는 현상, 환경 변수 설정 실수 피하기 (0) | 2024.07.16 |
---|---|
2차 개발이 시작되고 피그마 디자인이 ... 공통 컴포넌트 제작 어떻게 하지? (0) | 2024.07.16 |
쏟아지는 페이지들, 프론트엔드에서 어떻게 설계하면 좋을까? (0) | 2024.07.09 |