Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
ci:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.x'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run linter
run: npm run lint

- name: Type check
run: npx tsc --noEmit

- name: Build
run: npm run build
env:
NODE_ENV: production

4 changes: 2 additions & 2 deletions src/app/login/LoginContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const LoginContent = () => {
<input
type="email"
placeholder="이메일"
className="h-[41px] w-full rounded-lg border px-5 py-3 font-serif typo-regular-4 text-k-400 focus:outline-none"
className="w-full rounded-lg border border-k-100 px-5 py-3 font-serif typo-regular-4 text-k-400 focus:outline-none"
{...register("email", {
onChange: handleEmailChange,
})}
Expand All @@ -101,7 +101,7 @@ const LoginContent = () => {
<input
type="password"
placeholder="비밀번호"
className="h-[41px] w-full rounded-lg border px-5 py-3 font-serif typo-regular-4 text-k-400 focus:outline-none"
className="w-full rounded-lg border border-k-100 px-5 py-3 font-serif typo-regular-4 text-k-400 focus:outline-none"
{...register("password")}
onKeyDown={handleKeyDown}
/>
Expand Down
5 changes: 3 additions & 2 deletions src/app/sign-up/email/EmailSignUpForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ const EmailSignUpForm = () => {
onSuccess: (data) => {
router.push(`/sign-up?token=${data.signUpToken}`);
},
onError: (error: any) => {
toast.error(error.response?.data?.message || "회원가입에 실패했습니다.");
onError: (error: unknown) => {
const axiosError = error as { response?: { data?: { message?: string } } };
toast.error(axiosError.response?.data?.message || "회원가입에 실패했습니다.");
},
},
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";

import { validateLanguageScore } from "@/utils/scoreUtils";
import validateLanguageScore from "@/utils/scoreUtils";

import { LanguageTestEnum } from "@/types/score";

Expand Down
2 changes: 1 addition & 1 deletion src/components/button/BlockBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";

import { type VariantProps, cva } from "class-variance-authority";

import { cn } from "@/utils/designUtils";
import cn from "@/utils/designUtils";

const blockBtnVariants = cva("h-13 w-full min-w-80 max-w-screen-sm rounded-lg flex items-center justify-center", {
variants: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/button/RoundBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";

import { type VariantProps, cva } from "class-variance-authority";

import { cn } from "@/utils/designUtils";
import cn from "@/utils/designUtils";

const roundBtnVariants = cva("h-[2.375rem] w-[6.375rem] rounded-3xl px-4 py-2.5 ", {
variants: {
Expand Down
3 changes: 1 addition & 2 deletions src/components/login/signup/SignupPolicyScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,9 @@ const SignupPolicyScreen = ({ toNextStage }: SignupPolicyScreenProps) => {
<div className="max-w-app fixed bottom-14 w-full bg-white">
<div className="px-5">
<BlockBtn
className="mb-[29px]"
className={`mb-[29px] ${isChecked ? "bg-primary" : "bg-bg-800"}`}
disabled={!isChecked}
onClick={toNextStage}
className={isChecked ? "bg-primary" : "bg-bg-800"}
>
다음
</BlockBtn>
Expand Down
28 changes: 17 additions & 11 deletions src/components/login/signup/SignupSurvey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ const SignupSurvey = ({ baseNickname, baseEmail, baseProfileImageUrl }: SignupSu
try {
const result = await uploadImageMutation.mutateAsync(profileImageFile);
imageUrl = result.fileUrl;
} catch (err: any) {
console.error("Error", err.message);
} catch (err: unknown) {
const error = err as { message?: string };
console.error("Error", error.message);
// toast.error는 hook의 onError에서 이미 처리되므로 중복 호출 제거
}
}
Expand All @@ -89,19 +90,24 @@ const SignupSurvey = ({ baseNickname, baseEmail, baseProfileImageUrl }: SignupSu
toast.success("회원가입이 완료되었습니다.");
router.push("/");
},
onError: (error: any) => {
if (error.response) {
console.error("Axios response error", error.response);
toast.error(error.response.data?.message || "회원가입에 실패했습니다.");
onError: (error: unknown) => {
const axiosError = error as {
response?: { data?: { message?: string } };
message?: string;
};
if (axiosError.response) {
console.error("Axios response error", axiosError.response);
toast.error(axiosError.response.data?.message || "회원가입에 실패했습니다.");
} else {
console.error("Error", error.message);
toast.error(error.message || "회원가입에 실패했습니다.");
console.error("Error", axiosError.message);
toast.error(axiosError.message || "회원가입에 실패했습니다.");
}
},
});
} catch (err: any) {
console.error("Error", err.message);
toast.error(err.message || "회원가입에 실패했습니다.");
} catch (err: unknown) {
const error = err as { message?: string };
console.error("Error", error.message);
toast.error(error.message || "회원가입에 실패했습니다.");
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/search/UniversityFilterSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import UniversitySearchInput from "@/components/search/UniversitySearchInput";

import { RegionKo } from "@/types/university";

import { RegionOption } from "@/app/search/SearchContent";
import { RegionOption } from "@/app/search/SearchContent.tsx";
import { IconDownArrow, IconHatColor, IconHatGray, IconLocationColor, IconLocationGray } from "@/public/svgs/search";

interface UniversityFilterSectionProps {
Expand Down
2 changes: 1 addition & 1 deletion src/components/search/UniversityRegionTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react";

import { RegionKo } from "@/types/university";

import { RegionOption } from "@/app/search/SearchContent";
import { RegionOption } from "@/app/search/SearchContent.tsx";

interface UniversityRegionTabsProps {
regions: RegionOption[];
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/UniverSityCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Image from "next/image";
import Link from "next/link";

import { convertImageUrl } from "@/utils/fileUtils";
import { shortenLanguageTestName } from "@/utils/universityUtils";
import shortenLanguageTestName from "@/utils/universityUtils";

import CheveronRightFilled from "@/components/ui/icon/ChevronRightFilled";

Expand Down
4 changes: 3 additions & 1 deletion src/utils/designUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));

export default cn;
18 changes: 9 additions & 9 deletions src/utils/jwtUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
interface JwtPayload {
sub: number;
role: string;
iat: number;
exp: number;
}

export const isTokenExpired = (token: string | null): boolean => {
if (!token) return true;
try {
const payload = JSON.parse(atob(token.split(".")[1]));
const payload = JSON.parse(atob(token.split(".")[1])) as JwtPayload;
const currentTime = Math.floor(Date.now() / 1000);
return payload.exp < currentTime;
} catch (error) {
Expand All @@ -10,20 +17,13 @@ export const isTokenExpired = (token: string | null): boolean => {
}
};

interface JwtPayload {
sub: number;
role: string;
iat: number;
exp: number;
}

export const tokenParse = (token: string | null): JwtPayload | null => {
if (typeof window === "undefined") return null;

if (!token) return null;

try {
const payload: JwtPayload = JSON.parse(atob(token.split(".")[1]));
const payload = JSON.parse(atob(token.split(".")[1])) as JwtPayload;
return payload;
} catch (error) {
console.error("토큰 파싱 오류:", error);
Expand Down
4 changes: 3 additions & 1 deletion src/utils/scoreUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LanguageTestEnum } from "@/types/score";

export const validateLanguageScore = (testType: string, score: string) => {
const validateLanguageScore = (testType: string, score: string) => {
const numScore = Number(score);

if (testType === LanguageTestEnum.TOEIC) {
Expand Down Expand Up @@ -31,3 +31,5 @@ export const validateLanguageScore = (testType: string, score: string) => {
}
}
};

export default validateLanguageScore;
7 changes: 5 additions & 2 deletions src/utils/universityUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { SHORT_LANGUAGE_TEST } from "@/constants/application";

export const shortenLanguageTestName = (name: string) => {
const shortenLanguageTestName = (name: string): string | undefined => {
if (Object.prototype.hasOwnProperty.call(SHORT_LANGUAGE_TEST, name)) {
return SHORT_LANGUAGE_TEST[name];
return SHORT_LANGUAGE_TEST[name] as string;
}
return undefined;
};

export default shortenLanguageTestName;
5 changes: 3 additions & 2 deletions src/utils/useInfinityScroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type UseInfinityScrollProps = {
};

type UseInfinityScrollReturn = {
lastElementRef: (node: HTMLDivElement | null) => void;
lastElementRef: (node: Element | null) => void;
};

const useInfinityScroll = ({
Expand All @@ -25,7 +25,8 @@ const useInfinityScroll = ({
if (!node) return;

observerRef.current = new IntersectionObserver(
([entry]) => {
(entries) => {
const [entry] = entries;
if (entry.isIntersecting) {
fetchNextPage();
}
Expand Down
Loading