Skip to content

Commit 4660a53

Browse files
committed
✨ feat: Implement local SvelteKit to Hono API communication test
- Hono Backend: - Added `/api/greet` GET endpoint, returns a JSON message. - Configured CORS middleware to allow requests from the SvelteKit dev server (localhost:5173). - SvelteKit Frontend (`+page.svelte`): - Added a button to trigger the backend API call. - Implemented `fetch` request to the `/api/greet` endpoint on button click. - Displays the received message in an alert, with basic loading and error handling.
1 parent f2c4a67 commit 4660a53

File tree

6 files changed

+309
-35
lines changed

6 files changed

+309
-35
lines changed

backend/.eslintrc.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module.exports = {
2+
root: true, // 현재 설정 파일이 ESLint 설정의 루트임을 나타냄
3+
parser: '@typescript-eslint/parser', // TypeScript 파서 지정
4+
plugins: [
5+
'@typescript-eslint/eslint-plugin', // TypeScript 플러그인 사용
6+
],
7+
extends: [
8+
'eslint:recommended', // ESLint 추천 규칙
9+
'plugin:@typescript-eslint/recommended', // TypeScript 추천 규칙
10+
],
11+
env: {
12+
node: true, // Node.js 환경 (Hono 백엔드이므로)
13+
es2024: true, // ES2024 문법 사용 가능
14+
},
15+
parserOptions: {
16+
ecmaVersion: 'latest', // 최신 ECMAScript 버전 사용
17+
sourceType: 'module', // ES 모듈 사용
18+
project: './tsconfig.json', // 타입 정보를 사용하는 규칙을 위해 tsconfig.json 경로 지정
19+
},
20+
rules: {
21+
// 필요한 경우 여기에 커스텀 규칙을 추가하거나 기존 규칙을 덮어쓸 수 있습니다.
22+
// 예: '@typescript-eslint/no-unused-vars': 'warn',
23+
},
24+
ignorePatterns: ['dist', 'node_modules'], // ESLint 검사에서 제외할 폴더/파일
25+
};

backend/package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "backend",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"dev": "tsx watch src/index.ts"
8+
},
9+
"type": "module",
10+
"keywords": [],
11+
"author": "",
12+
"license": "ISC",
13+
"packageManager": "[email protected]",
14+
"dependencies": {
15+
"@hono/node-server": "^1.14.3",
16+
"hono": "^4.7.11"
17+
},
18+
"devDependencies": {
19+
"@types/node": "^22.15.29",
20+
"@typescript-eslint/eslint-plugin": "^8.33.0",
21+
"@typescript-eslint/parser": "^8.33.0",
22+
"eslint": "^9.18.0",
23+
"tsx": "^4.19.4",
24+
"typescript": "^5.0.0"
25+
}
26+
}

backend/src/index.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { serve } from '@hono/node-server'; // 로컬 Node.js 환경 실행용
2+
import { Hono } from 'hono';
3+
import { cors } from 'hono/cors'; // CORS 미들웨어 import
4+
5+
const app = new Hono();
6+
7+
// CORS 미들웨어 설정
8+
app.use(
9+
'/api/*', // /api/ 경로 하위의 모든 요청에 CORS 적용
10+
cors({
11+
origin: 'http://localhost:5173', // SvelteKit 개발 서버 주소 허용
12+
allowMethods: ['GET', 'POST', 'OPTIONS'], // 허용할 HTTP 메소드
13+
allowHeaders: ['Content-Type', 'Authorization'], // 허용할 요청 헤더
14+
})
15+
);
16+
17+
// 기존 루트 핸들러 (약간 수정하여 구분)
18+
app.get('/', (c) => {
19+
return c.text('Hello Hono from the backend root!');
20+
});
21+
22+
// 새로운 API 엔드포인트 추가
23+
app.get('/api/greet', (c) => {
24+
console.log('✅ GET /api/greet 요청 받음'); // 백엔드 터미널에 로그 출력
25+
const message = '안녕하세요! Hono 백엔드에서 보내는 메시지입니다. 👋';
26+
return c.json({ message: message }); // JSON 형태로 메시지 반환
27+
});
28+
29+
const port = 3001;
30+
console.log(`Backend server is running on http://localhost:${port}`);
31+
console.log(`API 엔드포인트 테스트: http://localhost:${port}/api/greet`);
32+
33+
34+
// 로컬 개발을 위해 포트를 지정하여 실행합니다.
35+
serve({
36+
fetch: app.fetch,
37+
port,
38+
});
39+
40+
// Vercel 배포 시에는 일반적으로 Hono 앱 인스턴스를 export 합니다.
41+
// export default app;

backend/tsconfig.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"module": "NodeNext",
5+
"moduleResolution": "NodeNext", // 또는 "Bundler"
6+
"outDir": "./dist",
7+
"rootDir": "./src",
8+
"esModuleInterop": true,
9+
"forceConsistentCasingInFileNames": true,
10+
"strict": true,
11+
"skipLibCheck": true,
12+
"resolveJsonModule": true,
13+
"sourceMap": true,
14+
// "baseUrl": "./src", // (선택 사항) 절대 경로 import를 위한 기본 경로 설정
15+
// "paths": { // (선택 사항) 경로 별칭 설정
16+
// "@/*": ["*"]
17+
// }
18+
},
19+
"include": [
20+
"src/**/*"
21+
], // 컴파일 대상에 src 폴더 내 모든 .ts 파일을 포함
22+
"exclude": [
23+
"node_modules",
24+
"dist"
25+
] // 컴파일 대상에서 제외할 폴더
26+
}

frontend/src/routes/+page.svelte

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,75 @@
1+
<script lang="ts">
2+
let isLoading = false;
3+
let apiMessageFromServer = '';
4+
let fetchError = '';
5+
6+
async function callBackendApi() {
7+
isLoading = true;
8+
fetchError = '';
9+
apiMessageFromServer = ''; // 이전 메시지 초기화
10+
11+
try {
12+
// 백엔드 API URL (Hono 서버가 3001 포트에서 실행 중이라고 가정)
13+
const response = await fetch('http://localhost:3001/api/greet');
14+
15+
if (!response.ok) {
16+
// HTTP 응답 상태가 ok(200-299)가 아닐 경우 오류 발생
17+
const errorText = await response.text(); // 오류 응답 본문 시도
18+
throw new Error(
19+
`API 요청 실패: ${response.status} ${response.statusText}. 서버 응답: ${errorText}`
20+
);
21+
}
22+
23+
// 응답을 JSON으로 파싱
24+
const data = await response.json();
25+
26+
// 'message' 필드의 값을 alert으로 표시
27+
alert(data.message);
28+
apiMessageFromServer = data.message; // 페이지에도 표시 (선택 사항)
29+
} catch (error: any) {
30+
console.error('백엔드 API 호출 중 오류 발생:', error);
31+
fetchError = error.message || '백엔드에서 데이터를 가져오는 데 실패했습니다.';
32+
alert(`오류: ${fetchError}`);
33+
} finally {
34+
isLoading = false;
35+
}
36+
}
37+
</script>
38+
139
<h1>Welcome to SvelteKit</h1>
240
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
41+
42+
<hr style="margin: 20px 0;" />
43+
44+
<h2>백엔드 API 호출 테스트</h2>
45+
<button on:click={callBackendApi} disabled={isLoading}>
46+
{isLoading ? '백엔드 호출 중...' : '백엔드 API 호출하기'}
47+
</button>
48+
49+
{#if apiMessageFromServer && !fetchError}
50+
<p style="margin-top: 10px; color: green;">
51+
<strong>서버로부터 받은 메시지:</strong>
52+
{apiMessageFromServer}
53+
</p>
54+
{/if}
55+
56+
{#if fetchError}
57+
<p style="margin-top: 10px; color: red;">
58+
<strong>오류 발생:</strong>
59+
{fetchError}
60+
</p>
61+
{/if}
62+
63+
<style>
64+
button {
65+
padding: 10px 15px;
66+
font-size: 1rem;
67+
cursor: pointer;
68+
border: 1px solid #ccc;
69+
border-radius: 4px;
70+
}
71+
button:disabled {
72+
opacity: 0.6;
73+
cursor: not-allowed;
74+
}
75+
</style>

0 commit comments

Comments
 (0)