Compare commits
2 Commits
420c481b84
...
3eb5974414
| Author | SHA1 | Date | |
|---|---|---|---|
| 3eb5974414 | |||
| a573d7ffb1 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -46,3 +46,7 @@ next-env.d.ts
|
|||||||
bun.lockb
|
bun.lockb
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
pnpm-workspace.yaml
|
pnpm-workspace.yaml
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|||||||
@ -6,7 +6,12 @@ const nextConfig: NextConfig = {
|
|||||||
sassOptions: {
|
sassOptions: {
|
||||||
includePaths: [path.join(__dirname, './src/styles')],
|
includePaths: [path.join(__dirname, './src/styles')],
|
||||||
},
|
},
|
||||||
serverExternalPackages: ['@react-pdf/renderer'],
|
serverExternalPackages: ['@react-pdf/renderer', 'pino'],
|
||||||
|
logging: {
|
||||||
|
fetches: {
|
||||||
|
fullUrl: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"next": "15.2.4",
|
"next": "15.2.4",
|
||||||
"nodemailer": "^7.0.3",
|
"nodemailer": "^7.0.3",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
|
"pino": "^9.7.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-to-pdf": "^2.0.0",
|
"react-to-pdf": "^2.0.0",
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server'
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
|
import { HttpStatusCode } from 'axios'
|
||||||
import { prisma } from '@/libs/prisma'
|
import { prisma } from '@/libs/prisma'
|
||||||
|
import { writeLog } from '@/libs/logger'
|
||||||
import { type Suitable } from '@/types/Suitable'
|
import { type Suitable } from '@/types/Suitable'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,6 +44,7 @@ import { type Suitable } from '@/types/Suitable'
|
|||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
|
let responseStatus: number = HttpStatusCode.InternalServerError
|
||||||
try {
|
try {
|
||||||
const searchParams = request.nextUrl.searchParams
|
const searchParams = request.nextUrl.searchParams
|
||||||
const pageNumber = parseInt(searchParams.get('pageNumber') || '0')
|
const pageNumber = parseInt(searchParams.get('pageNumber') || '0')
|
||||||
@ -51,7 +54,8 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
/* 파라미터 체크 */
|
/* 파라미터 체크 */
|
||||||
if (pageNumber === 0 || itemPerPage === 0) {
|
if (pageNumber === 0 || itemPerPage === 0) {
|
||||||
return NextResponse.json({ error: '페이지 번호와 페이지당 아이템 수가 필요합니다' }, { status: 400 })
|
responseStatus = HttpStatusCode.BadRequest
|
||||||
|
return NextResponse.json({ error: '페이지 번호와 페이지당 아이템 수가 필요합니다' }, { status: responseStatus })
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = `
|
let query = `
|
||||||
@ -104,6 +108,7 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
const suitable: Suitable[] = await prisma.$queryRawUnsafe(query, pageNumber, itemPerPage)
|
const suitable: Suitable[] = await prisma.$queryRawUnsafe(query, pageNumber, itemPerPage)
|
||||||
|
|
||||||
|
responseStatus = HttpStatusCode.Ok
|
||||||
return NextResponse.json(suitable, {
|
return NextResponse.json(suitable, {
|
||||||
headers: {
|
headers: {
|
||||||
'spinner-state': 'true',
|
'spinner-state': 'true',
|
||||||
@ -111,6 +116,9 @@ export async function GET(request: NextRequest) {
|
|||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`데이터 조회 중 오류가 발생했습니다: ${error}`)
|
console.error(`데이터 조회 중 오류가 발생했습니다: ${error}`)
|
||||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: 500 })
|
responseStatus = HttpStatusCode.InternalServerError
|
||||||
|
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: responseStatus })
|
||||||
|
} finally {
|
||||||
|
await writeLog(request, responseStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/libs/logger.ts
Normal file
51
src/libs/logger.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { NextRequest } from 'next/server'
|
||||||
|
import { join } from 'path'
|
||||||
|
import pino from 'pino'
|
||||||
|
|
||||||
|
/* 로그 파일 경로 */
|
||||||
|
const logFilePath = join('.', 'logs', 'onsite-survey.log')
|
||||||
|
|
||||||
|
/* 로거 생성 함수 */
|
||||||
|
const createLogger = (): pino.Logger => {
|
||||||
|
try {
|
||||||
|
return pino({
|
||||||
|
level: 'info',
|
||||||
|
timestamp: pino.stdTimeFunctions.isoTime,
|
||||||
|
transport: {
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
target: 'pino/file',
|
||||||
|
options: {
|
||||||
|
destination: logFilePath,
|
||||||
|
mkdir: true,
|
||||||
|
sync: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Pino transport 생성 실패, 기본 로거 사용:', error)
|
||||||
|
return pino({
|
||||||
|
level: 'info',
|
||||||
|
timestamp: pino.stdTimeFunctions.isoTime,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 로거 인스턴스 */
|
||||||
|
export const logger = createLogger()
|
||||||
|
|
||||||
|
/* 로그 기록 함수 */
|
||||||
|
export const writeLog = async (request: NextRequest, responseStatus: number): Promise<void> => {
|
||||||
|
const logData = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
status: responseStatus,
|
||||||
|
method: request.method,
|
||||||
|
url: request.url,
|
||||||
|
// headers: Object.fromEntries(request.headers),
|
||||||
|
query: Object.fromEntries(new URL(request.url).searchParams),
|
||||||
|
body: request.body ? await request.clone().text() : undefined,
|
||||||
|
}
|
||||||
|
logger.info(logData, 'API Request')
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user