-
Notifications
You must be signed in to change notification settings - Fork 3
Feature/#168 사람인 데이터 update #169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
The head ref may contain hidden characters: "Feature/#168-\uC0AC\uB78C\uC778-\uB370\uC774\uD130-Update"
Changes from all commits
0c1f8a2
cd209f1
30ea01d
16fe5cf
2f70a5e
21dc077
23fa416
7d049c8
7d34829
6f3be0f
c66af01
8dd94b2
b0f4092
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| 'use client'; | ||
|
|
||
| import AdminButton from '@/features/admin/admin-button'; | ||
| import Image from 'next/image'; | ||
| import { useState } from 'react'; | ||
|
|
||
| const AdminPage = () => { | ||
| const [hovered, setHovered] = useState(false); | ||
|
|
||
| return ( | ||
| <div | ||
| className='flex h-full items-center justify-center' | ||
| style={{ | ||
| background: 'radial-gradient(circle, #E55A27 30%, #ffffff 100%)', | ||
| }} | ||
| > | ||
| <section | ||
| className='group relative flex items-center gap-10 rounded-3xl border border-white/20 p-10 text-white shadow-2xl backdrop-blur-md' | ||
| style={{ | ||
| backgroundColor: '#C2410C', | ||
| }} | ||
| onMouseEnter={() => setHovered(true)} | ||
| onMouseLeave={() => setHovered(false)} | ||
| > | ||
| <AdminButton /> | ||
| <div className='group relative'> | ||
| <Image | ||
| src='/assets/character/card/admin-character.png' | ||
| width={200} | ||
| height={200} | ||
| alt='레옹병아리' | ||
| className='rounded-full border-4 border-white transition-transform duration-500 group-hover:scale-105' | ||
| style={{ | ||
| boxShadow: ` | ||
| 0 0 10px rgba(255, 255, 255, 0.3), | ||
| 0 0 20px rgba(226, 86, 39, 0.5), | ||
| 0 0 40px rgba(194, 65, 12, 0.5) | ||
| `, | ||
| }} | ||
| /> | ||
| <div className='absolute inset-0 animate-ping rounded-full border-4 border-yellow-300 opacity-30 group-hover:opacity-70'></div> | ||
| </div> | ||
| </section> | ||
|
|
||
| <div | ||
| className='absolute left-1/2 top-40 mb-12 -translate-x-1/2 transform animate-pulse rounded-xl bg-black/70 p-6 text-2xl text-yellow-400 shadow-xl' | ||
| style={{ | ||
| width: 'max-content', | ||
| transition: 'opacity 0.3s ease', | ||
| }} | ||
| > | ||
| <p>일주일에 한번만 눌러라 마!</p> | ||
| <p>안 그럼 총 맞는다!</p> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default AdminPage; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,111 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ENV } from '@/constants/env-constants'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AUTH_MESSAGE, DB_MESSAGE } from '@/constants/message-constants'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getToken } from 'next-auth/jwt'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { NextRequest, NextResponse } from 'next/server'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { eduMap, jobMidCdMap, locMcdMap } from '@/features/admin/data/saramin-constants'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { prisma } from '@/lib/prisma'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { JobPosting } from '@prisma/client'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ERROR: { DB_SERVER_ERROR }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SUCCESS: { CREATE_SUCCESS }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = DB_MESSAGE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ERROR: { EXPIRED_TOKEN }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = AUTH_MESSAGE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { NEXTAUTH_SECRET, SARAMIN_API_KEY } = ENV; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const URL = 'https://oapi.saramin.co.kr/job-search'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const COUNT = 100; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PAGE = '0'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DEFAULT_EDU_CODE = 6; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const LIMIT_EDU_LEVEL = 5; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type JobRecord = Omit<JobPosting, 'id' | 'createdAt'>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * PATCH 요청 함수 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const PATCH = async (request: NextRequest) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const token = await getToken({ req: request, secret: NEXTAUTH_SECRET }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!token) return NextResponse.json({ message: EXPIRED_TOKEN }, { status: 401 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const allRecords: JobRecord[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const [jobCdStr, _] of Object.entries(jobMidCdMap)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const [_n, locCodes] of Object.entries(locMcdMap)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const params = new URLSearchParams({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'access-key': SARAMIN_API_KEY as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loc_mcd: locCodes, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| job_mid_cd: jobCdStr, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start: PAGE, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| count: COUNT.toString(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const res = await fetch(`${URL}?${params}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const data = await res.json(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const jobs = data.jobs.job; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobs.forEach((job: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기 보면 for문 안에 또 for문이 있고 그 안에 또 forEach가 있어서 지금 3중으로 돌고있는데
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금 단계에서 테스트를 실행하면 안됩니다! 대략 12,000개의 데이터입니다. 그러나 주석에 말씀드린 것처럼 이후에 로직을 수정할 예정입니다. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const pos = job.position || {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const rawEd = pos['required-education-level']?.code; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let eduCode: number = DEFAULT_EDU_CODE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (rawEd !== undefined) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const i = Number(rawEd); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eduCode = i <= LIMIT_EDU_LEVEL ? i : eduMap[i]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allRecords.push({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: job.url as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| companyName: job.company?.detail?.name as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| positionTitle: pos.title as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| locationName: pos.location?.name as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| industryName: pos.industry?.name as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keyword: job.keyword as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobMidCodeName: pos['job-mid-code']?.name as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| experienceCode: Number(pos['experience-level']?.code), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| experienceName: pos['experience-level']?.name as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requiredEducationCode: eduCode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requiredEducationName: pos['required-education-level']?.name as string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| openingTimestamp: Number(job['opening-timestamp']), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expirationTimestamp: Number(job['expiration-timestamp']), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await new Promise((_) => setTimeout(_, 300)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAPI 호출 최적화 및 에러 처리 개선 필요 현재 코드는 3중 중첩 루프(for-for-forEach)로 API를 호출하고 있어 요청 수가 많고 효율성이 떨어집니다. 또한 API 응답 유효성 검사나 에러 처리가 부족합니다. - for (const [jobCdStr, _] of Object.entries(jobMidCdMap)) {
- for (const [_n, locCodes] of Object.entries(locMcdMap)) {
- const params = new URLSearchParams({
- 'access-key': SARAMIN_API_KEY as string,
- loc_mcd: locCodes,
- job_mid_cd: jobCdStr,
- start: PAGE,
- count: COUNT.toString(),
- });
- const res = await fetch(`${URL}?${params}`);
- const data = await res.json();
- const jobs = data.jobs.job;
-
- jobs.forEach((job: any) => {
+ // API 요청을 병렬로 처리하기 위한 프로미스 배열
+ const fetchPromises = [];
+
+ for (const [jobCdStr, _] of Object.entries(jobMidCdMap)) {
+ for (const [_n, locCodes] of Object.entries(locMcdMap)) {
+ // 각 조합에 대한 API 요청 함수 생성
+ fetchPromises.push(
+ fetchJobsFromSaramin(jobCdStr, locCodes)
+ .then(jobs => {
+ if (jobs && jobs.length > 0) {
+ const mappedJobs = jobs.map(job => mapJobToRecord(job));
+ allRecords.push(...mappedJobs);
+ }
+ })
+ .catch(error => {
+ console.error(`API 요청 실패 (job: ${jobCdStr}, loc: ${locCodes}):`, error);
+ })
+ );
+ }
+ }
+
+ // 모든 API 요청을 병렬로 처리 (적절한 타임아웃 설정)
+ await Promise.all(
+ fetchPromises.map(promise =>
+ Promise.race([
+ promise,
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('API 요청 타임아웃')), 10000)
+ )
+ ])
+ )
+ );
+ // fetchJobsFromSaramin 함수 정의
+ async function fetchJobsFromSaramin(jobCdStr: string, locCodes: string) {
+ // 요청 간 적절한 딜레이
+ await new Promise(resolve => setTimeout(resolve, 300));
+
+ const params = new URLSearchParams({
+ 'access-key': SARAMIN_API_KEY as string,
+ loc_mcd: locCodes,
+ job_mid_cd: jobCdStr,
+ start: PAGE,
+ count: COUNT.toString(),
+ });
+
+ try {
+ const res = await fetch(`${URL}?${params}`);
+
+ if (!res.ok) {
+ throw new Error(`API 응답 오류: ${res.status}`);
+ }
+
+ const data = await res.json();
+
+ if (!data.jobs?.job) {
+ return [];
+ }
+
+ return data.jobs.job;
+ } catch (error) {
+ console.error(`Saramin API 요청 실패:`, error);
+ return [];
+ }
+ }
+
+ // mapJobToRecord 함수 정의
+ function mapJobToRecord(job: any): JobRecord {
+ const pos = job.position || {};
+ const rawEd = pos['required-education-level']?.code;
+ let eduCode: number = DEFAULT_EDU_CODE;
+
+ if (rawEd !== undefined) {
+ const i = Number(rawEd);
+ eduCode = i <= LIMIT_EDU_LEVEL ? i : (eduMap[i] || DEFAULT_EDU_CODE);
+ }
+
+ return {
+ url: job.url as string || '',
+ companyName: job.company?.detail?.name as string || '',
+ positionTitle: pos.title as string || '',
+ locationName: pos.location?.name as string || '',
+ industryName: pos.industry?.name as string || '',
+ keyword: job.keyword as string || '',
+ jobMidCodeName: pos['job-mid-code']?.name as string || '',
+ experienceCode: Number(pos['experience-level']?.code) || 0,
+ experienceName: pos['experience-level']?.name as string || '',
+ requiredEducationCode: eduCode,
+ requiredEducationName: pos['required-education-level']?.name as string || '',
+ openingTimestamp: Number(job['opening-timestamp']) || 0,
+ expirationTimestamp: Number(job['expiration-timestamp']) || 0,
+ };
+ }각 API 요청을 병렬로 처리하고 타임아웃을 적용하여 효율성을 높입니다. 또한, 에러 처리와 기본값 설정을 강화하여 데이터 일관성을 유지합니다. 다음 스크립트로 API 요청의 정확한 형태를 확인할 수 있습니다: 🏁 Script executed: #!/bin/bash
# API 호출 최적화를 위한 정보 확인
# 사람인 상수 파일 확인
echo "사람인 API 관련 상수:"
cat src/features/admin/data/saramin-constants.ts
# 총 API 호출 횟수 계산
echo "\n총 API 호출 횟수 계산:"
JOB_COUNT=$(grep -o "{" src/features/admin/data/saramin-constants.ts | wc -l | xargs)
LOC_COUNT=$(grep -o ":" src/features/admin/data/saramin-constants.ts | wc -l | xargs)
echo "직군 수: $JOB_COUNT, 지역 수: $LOC_COUNT, 총 API 호출 수: $((JOB_COUNT * LOC_COUNT))"Length of output: 1103 API 호출 병렬 처리 및 에러 핸들링 강화 필요 현재
이를 개선하기 위해 다음 조치를 권장합니다:
수정 대상 위치
위 개선을 통해 전체 처리 시간을 크게 단축하고, 코드 가독성·유지보수성을 확보할 수 있습니다. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 기존 jobPosting 데이터 전부 삭제 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await prisma.jobPosting.deleteMany({}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 새 데이터 삽입 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await prisma.jobPosting.createMany({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: allRecords.map((record) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: record.url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| companyName: record.companyName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| positionTitle: record.positionTitle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| locationName: record.locationName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| industryName: record.industryName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keyword: record.keyword, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobMidCodeName: record.jobMidCodeName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| experienceCode: record.experienceCode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| experienceName: record.experienceName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requiredEducationCode: record.requiredEducationCode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requiredEducationName: record.requiredEducationName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| openingTimestamp: Number(record.openingTimestamp), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expirationTimestamp: Number(record.expirationTimestamp), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| skipDuplicates: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+80
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 트랜잭션 처리 및 데이터 유효성 검증 추가 필요 현재 코드는 모든 기존 데이터를 삭제한 후 새 데이터를 삽입하는데, 이 과정에서 오류가 발생하면 데이터가 모두 손실될 수 있습니다. 트랜잭션 처리와 데이터 유효성 검증이 필요합니다. - // 기존 jobPosting 데이터 전부 삭제
- await prisma.jobPosting.deleteMany({});
-
- // 새 데이터 삽입
- await prisma.jobPosting.createMany({
- data: allRecords.map((record) => ({
- url: record.url,
- companyName: record.companyName,
- positionTitle: record.positionTitle,
- locationName: record.locationName,
- industryName: record.industryName,
- keyword: record.keyword,
- jobMidCodeName: record.jobMidCodeName,
- experienceCode: record.experienceCode,
- experienceName: record.experienceName,
- requiredEducationCode: record.requiredEducationCode,
- requiredEducationName: record.requiredEducationName,
- openingTimestamp: Number(record.openingTimestamp),
- expirationTimestamp: Number(record.expirationTimestamp),
- })),
- skipDuplicates: true,
- });
+ // 데이터 유효성 검증
+ if (allRecords.length === 0) {
+ return NextResponse.json({
+ message: '가져온 채용 공고 데이터가 없습니다. 사람인 API 응답을 확인해주세요.'
+ }, { status: 400 });
+ }
+
+ // 트랜잭션으로 데이터 처리
+ const result = await prisma.$transaction(async (tx) => {
+ // 기존 jobPosting 데이터 개수 확인
+ const existingCount = await tx.jobPosting.count();
+
+ // 기존 데이터 삭제
+ await tx.jobPosting.deleteMany({});
+
+ // 유효한 레코드만 필터링
+ const validRecords = allRecords.filter(record =>
+ record.url && record.companyName && record.positionTitle
+ );
+
+ // 새 데이터 삽입
+ const insertResult = await tx.jobPosting.createMany({
+ data: validRecords,
+ skipDuplicates: true,
+ });
+
+ return {
+ beforeCount: existingCount,
+ insertedCount: insertResult.count,
+ totalRecords: allRecords.length,
+ validRecords: validRecords.length
+ };
+ });📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. message 같은 경우 response로 바꾸지 않고 바로 보내주고 있으니, 밑쪽에 바로 적용하셔도 될 것 같습니다. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: CREATE_SUCCESS, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ response }, { status: 200 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ message: DB_SERVER_ERROR }, { status: 500 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+108
to
+110
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 에러 처리 개선 필요 현재 에러 처리가 최소한으로만 되어 있습니다. 에러 로깅과 더 자세한 에러 메시지를 제공하는 것이 도움이 됩니다. try {
// 기존 코드...
} catch (error) {
+ console.error('Admin API 에러:', error);
+
+ // 에러 타입에 따른 대응
+ if (error instanceof Error) {
+ return NextResponse.json({
+ message: `${DB_SERVER_ERROR}: ${error.message}`
+ }, { status: 500 });
+ }
return NextResponse.json({ message: DB_SERVER_ERROR }, { status: 500 });
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| 'use client'; | ||
|
|
||
| import Button from '@/components/ui/button'; | ||
| import { postJobPostingDataToDatabase } from '@/features/admin/api/client-services'; | ||
| import { showNotiflixConfirm } from '@/utils/show-notiflix-confirm'; | ||
|
|
||
| const AdminButton = () => { | ||
| const handleOnClick = () => { | ||
| showNotiflixConfirm({ | ||
| message: '이번 업데이트 차례가 당신이 맞습니까? 진짜로??', | ||
| okFunction: async () => { | ||
| await postJobPostingDataToDatabase(); | ||
| }, | ||
| }); | ||
| }; | ||
| return ( | ||
| <div> | ||
| <Button onClick={handleOnClick}>DB PATCH</Button> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default AdminButton; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { API_METHOD } from '@/constants/api-method-constants'; | ||
| import { ROUTE_HANDLER_PATH } from '@/constants/path-constant'; | ||
| import { fetchWithSentry } from '@/utils/fetch-with-sentry'; | ||
| import { Notify } from 'notiflix'; | ||
|
|
||
| const { ROOT } = ROUTE_HANDLER_PATH.ADMIN; | ||
| const { PATCH } = API_METHOD; | ||
|
|
||
| /** | ||
| * | ||
| * @description 사람인 데이터를 정제하여 DB로 보내는 로직 | ||
| */ | ||
| export const postJobPostingDataToDatabase = async (): Promise<void> => { | ||
| const { response } = await fetchWithSentry(ROOT, { | ||
| method: PATCH, | ||
| }); | ||
|
|
||
| Notify.success(response.message); | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,34 @@ | ||||||||||||||||||||||||||||||||||||
| export const jobMidCdMap: Record<number, string> = { | ||||||||||||||||||||||||||||||||||||
| 16: '기획·전략', | ||||||||||||||||||||||||||||||||||||
| 14: '마케팅·홍보·조사', | ||||||||||||||||||||||||||||||||||||
| 3: '회계·세무·재무', | ||||||||||||||||||||||||||||||||||||
| 5: '인사·노무·HRD', | ||||||||||||||||||||||||||||||||||||
| 4: '총무·법무·사무', | ||||||||||||||||||||||||||||||||||||
| 2: 'IT개발·데이터', | ||||||||||||||||||||||||||||||||||||
| 15: '디자인', | ||||||||||||||||||||||||||||||||||||
| 8: '영업·판매·무역', | ||||||||||||||||||||||||||||||||||||
| 21: '고객상담·TM', | ||||||||||||||||||||||||||||||||||||
| 18: '구매·자재·물류', | ||||||||||||||||||||||||||||||||||||
| 12: '상품기획·MD', | ||||||||||||||||||||||||||||||||||||
| 7: '운전·운송·배송', | ||||||||||||||||||||||||||||||||||||
| 10: '서비스', | ||||||||||||||||||||||||||||||||||||
| 11: '생산', | ||||||||||||||||||||||||||||||||||||
| 22: '건설·건축', | ||||||||||||||||||||||||||||||||||||
| 6: '의료', | ||||||||||||||||||||||||||||||||||||
| 9: '연구·R&D', | ||||||||||||||||||||||||||||||||||||
| 19: '교육', | ||||||||||||||||||||||||||||||||||||
| 13: '미디어·문화·스포츠', | ||||||||||||||||||||||||||||||||||||
| 17: '금융·보험', | ||||||||||||||||||||||||||||||||||||
| 20: '공공·복지', | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| export const locMcdMap: Record<string, string> = { | ||||||||||||||||||||||||||||||||||||
| '수도권': '101000,102000,108000', | ||||||||||||||||||||||||||||||||||||
| '경상도': '110000,111000,104000,106000,107000', | ||||||||||||||||||||||||||||||||||||
| '전라도': '112000,113000,103000', | ||||||||||||||||||||||||||||||||||||
| '충청도': '115000,114000,105000,118000', | ||||||||||||||||||||||||||||||||||||
| '강원도': '109000', | ||||||||||||||||||||||||||||||||||||
| '제주도': '116000', | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| export const eduMap: Record<number, number> = { 6: 1, 7: 2, 8: 3, 9: 4 }; | ||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 교육 코드 매핑에 타입 정의와 주석 추가 필요 교육 코드 매핑은 간결하게 구현되었으나, 타입 정의와 설명 주석이 없어 코드의 의미를 이해하기 어렵습니다. 각 코드가 어떤 교육 수준을 나타내는지 주석으로 설명하면 좋을 것 같습니다. +/**
+ * 사람인 API의 교육 코드를 내부 교육 코드로 매핑한 객체
+ * 6: 고졸 -> 1: 고졸
+ * 7: 초대졸 -> 2: 초대졸
+ * 8: 대졸 -> 3: 대졸
+ * 9: 석박사 -> 4: 석박사
+ */
-export const eduMap: Record<number, number> = { 6: 1, 7: 2, 8: 3, 9: 4 };
+export const eduMap: Record<SaraminEduCode, InternalEduCode> = { 6: 1, 7: 2, 8: 3, 9: 4 };
+
+type SaraminEduCode = number;
+type InternalEduCode = number;📝 Committable suggestion
Suggested change
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 코드래빗이 이미 잘 달어주었네요, eduMap에서 각각의 숫자들이 무엇을 의미하는지 변수로 지정하거나 주석을 달아주시면 좋을 것 같습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @parkminjo 네, eduMap의 각 숫자들의 의미를 더 명확하게 표현하기 위해 enum을 사용하는 방법을 제안드립니다. 이렇게 하면 숫자의 의미가 코드 자체에서 명확해지고 타입 안전성도 높아집니다: /**
* 사람인 API 및 내부 시스템에서 사용하는 교육 수준 코드
*/
enum SaraminEduCode {
HIGH_SCHOOL = 6, // 고졸
COLLEGE = 7, // 초대졸
UNIVERSITY = 8, // 대졸
GRADUATE = 9 // 석박사
}
enum InternalEduCode {
HIGH_SCHOOL = 1, // 고졸
COLLEGE = 2, // 초대졸
UNIVERSITY = 3, // 대졸
GRADUATE = 4 // 석박사
}
/**
* 사람인 API의 교육 코드를 내부 교육 코드로 매핑하는 객체
*/
export const eduMap: Record<SaraminEduCode, InternalEduCode> = {
[SaraminEduCode.HIGH_SCHOOL]: InternalEduCode.HIGH_SCHOOL,
[SaraminEduCode.COLLEGE]: InternalEduCode.COLLEGE,
[SaraminEduCode.UNIVERSITY]: InternalEduCode.UNIVERSITY,
[SaraminEduCode.GRADUATE]: InternalEduCode.GRADUATE
};이렇게 구현하면 숫자 값이 무엇을 의미하는지 명확하게 표현되고, 나중에 코드를 읽는 사람이 바로 이해할 수 있습니다. 또한 타입 체크도 더 엄격하게 이루어집니다. 🐰✏️ ✏️ Learnings added
|
||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
경고 메시지의 톤과 표현 개선 필요
현재 경고 메시지가 비공식적이고 위협적인 톤으로 작성되어 있습니다. 관리자 페이지에서는 더 전문적이고 정보를 제공하는 메시지가 적절합니다.
<div className='absolute left-1/2 top-40 mb-12 -translate-x-1/2 transform animate-pulse rounded-xl bg-black/70 p-6 text-2xl text-yellow-400 shadow-xl' style={{ width: 'max-content', transition: 'opacity 0.3s ease', }} > - <p>일주일에 한번만 눌러라 마!</p> - <p>안 그럼 총 맞는다!</p> + <p>주의: 데이터 업데이트는 일주일에 한 번만 실행해주세요.</p> + <p>불필요한 API 호출을 방지하기 위한 권장사항입니다.</p> </div>📝 Committable suggestion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헉,,,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ImJaeOne 놀라셨군요! 제안이 너무 형식적이었을 수 있습니다. 만약 이 관리자 페이지가 내부용이고 팀 내에서 재미있는 말투를 선호하신다면, 원래 메시지를 유지하셔도 괜찮습니다. 제 제안은 일반적인 사용자 인터페이스 가이드라인에 기반한 것이었습니다만, 내부 도구라면 팀 문화에 맞는 재미있는 메시지가 더 적합할 수 있습니다. 어떤 스타일을 유지하고 싶으신지 알려주시면 도움드리겠습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ㅋㅋㅋ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.