Repository: Team-B1ND/dodamdodam-teacher-web
Pull request #143: Origin/feat/#142 night study project students
Author: login: dlskl
Created: 2025-05-13T11:00:49Z | Merged: 2025-05-14T08:47:00Z

## ⛳️ 작업 내용

- 프로젝트 심자중인 학생 리스트
- 프로젝트 심자중인 학생 엑셀(csv) 출력
- 프로젝트 이름 별 필터링 (select) 추가

<!-- 작업한 사항을 간략하게 적어주세요 -->

## 📸 스크린샷
<img width="1470" alt="스크린샷 2025-05-13 오후 7 59 57" src="https://github.com/user-attachments/assets/3b9a6f7d-de3d-49b1-b3b2-6cfc4cccf3db" />
<img width="407" alt="스크린샷 2025-05-13 오후 8 00 20" src="https://github.com/user-attachments/assets/79a3a2a1-7a72-4647-884f-1080e0fe7c7e" />


<!-- 스크린샷으로 작업한 사항을 보여주세요 -->



Diff:
diff --git a/src/components/NightStudy/NightStudyAllow/style.ts b/src/components/NightStudy/NightStudyAllow/style.ts
index b1085d74..24a067c3 100644
--- a/src/components/NightStudy/NightStudyAllow/style.ts
+++ b/src/components/NightStudy/NightStudyAllow/style.ts
@@ -1,4 +1,5 @@
 import styled, { CSSObject } from "styled-components";
+import { DodamTypography } from "@b1nd/dds-web";
 
 export const NightStudyHeaderContainer = styled.div`
   display: flex;
@@ -15,8 +16,8 @@ export const SelectContainer = styled.div`
   gap: 5px;
 `;
 export const InfoText = styled.p`
-  font-size: 12px;
-  color: red;
+  ${DodamTypography.Body2.Regular}
+  color: ${({ theme }) => theme.statusNegative};
 
   margin: 4px 0px 0px 10px;
 `;
diff --git a/src/components/NightStudy/NightStudyToday/NightStudyProjectToday/index.tsx b/src/components/NightStudy/NightStudyToday/NightStudyProjectToday/index.tsx
new file mode 100644
index 00000000..717508c7
--- /dev/null
+++ b/src/components/NightStudy/NightStudyToday/NightStudyProjectToday/index.tsx
@@ -0,0 +1,56 @@
+import { TBody, TD, TR } from "@b1nd/b1nd-dodamdodam-ui";
+import { NightStudyTBody,NightStudyTR } from "../NightStudyTodayItem/style";
+import { useGetNightStudyProjectStudents } from "queries/NightStudy/nightstudy.query";
+import { TDNameStyle, TDProjectNameStyle, TDRoomStyle, TDStudentStyle } from "./style";
+import { NightStudyStudentFillter } from "utils/NightStudy/NightStudyStudentFillter";
+
+interface NightStudyTodayProps {
+    studentName: string;
+    NightStudyGrade: number;
+    selectRoom: string;
+    projectName : string;
+}
+
+const NightStudyProjectItem = ({
+    studentName,
+    NightStudyGrade,
+    selectRoom,
+    projectName
+  }: NightStudyTodayProps) => {
+  const { data:ProjectStudents } = useGetNightStudyProjectStudents({ suspense: true });
+
+  return (
+    <>
+      <TBody customStyle={NightStudyTBody}>
+        {NightStudyStudentFillter(ProjectStudents, studentName, NightStudyGrade, selectRoom, projectName)?.map((project) => (
+          <TR key={project.id} customStyle={NightStudyTR}>
+            <TD
+              customStyle={TDProjectNameStyle}
+            >
+              {project.projectName}
+            </TD>
+            <TD
+              customStyle={TDNameStyle}
+            >
+              {project.name}
+            </TD>
+            <TD
+              customStyle={TDStudentStyle}
+            >
+              {project.grade}
+              {project.room}
+              {project.number > 10 ? project.number : `0${project.number}`}
+            </TD>
+            <TD
+              customStyle={TDRoomStyle}
+            >
+              {project.projectRoom}
+            </TD>
+          </TR>
+        ))}
+      </TBody>
+    </>
+  );
+};
+
+export default NightStudyProjectItem;
diff --git a/src/components/NightStudy/NightStudyToday/NightStudyProjectToday/style.ts b/src/components/NightStudy/NightStudyToday/NightStudyProjectToday/style.ts
new file mode 100644
index 00000000..3d576cbe
--- /dev/null
+++ b/src/components/NightStudy/NightStudyToday/NightStudyProjectToday/style.ts
@@ -0,0 +1,31 @@
+import type { CSSObject } from "styled-components";
+
+const TRWrapStyles: CSSObject = {
+  overflow: "hidden",
+  textOverflow: "ellipsis",
+  whiteSpace: "nowrap"
+};
+
+export const TDProjectNameStyle: CSSObject = {
+  ...TRWrapStyles,
+  textAlign: "left",
+  width: "180px"
+};
+
+export const TDNameStyle: CSSObject = {
+  ...TRWrapStyles,
+  textAlign: "left",
+  width: "160px",
+};
+
+export const TDStudentStyle: CSSObject = {
+  ...TRWrapStyles,
+  textAlign: "left",
+  width: "130px",
+};
+
+export const TDRoomStyle: CSSObject = {
+  ...TRWrapStyles,
+  textAlign:"center",
+  width: "120px",
+};
\ No newline at end of file
diff --git a/src/components/NightStudy/NightStudyToday/index.tsx b/src/components/NightStudy/NightStudyToday/index.tsx
index 4d9a339a..7e7a5bc9 100644
--- a/src/components/NightStudy/NightStudyToday/index.tsx
+++ b/src/components/NightStudy/NightStudyToday/index.tsx
@@ -3,9 +3,12 @@ import * as S from "./style";
 import { Suspense, useState } from "react";
 import { GRADE_ITEMS } from "constants/Grade/grade.constant";
 import { useRecoilState } from "recoil";
-import { NightStudyGrade } from "stores/NightStudy/nightstudy.store";
+import { NightStudyGrade, NightStudyProjectSelectAtom } from "stores/NightStudy/nightstudy.store";
 import TableAttribute from "components/common/TableAttribute";
-import { NIGHTSTUDY_ALLOW_ITEMS } from "constants/LateNight/latenight.constant";
+import {
+  NIGHTSTUDY_ALLOW_ITEMS,
+  PROJECT_NIGHTSTUDY_STUDENTS_ITEM,
+} from "constants/LateNight/latenight.constant";
 import ErrorBoundary from "components/common/ErrorBoundary";
 import NightStudyTodayItem from "./NightStudyTodayItem";
 import { changeGrade } from "utils/Member/changeGrade";
@@ -13,57 +16,132 @@ import CsvButton from "components/common/ExtractCsvData";
 import { useNightStudyStudentList } from "hooks/NightStudy/useNightStudyStudentList";
 import dayjs from "dayjs";
 import { PointSelectRoom } from "stores/Point/point.store";
+import { DodamSegmentedButton } from "@b1nd/dds-web";
+import NightStudyProjectItem from "./NightStudyProjectToday";
+import { useNightStudyProjectStudentsList } from "hooks/NightStudy/useNightStudyProjectStudentsList";
+import { useGetNightStudyProjectStudents } from "queries/NightStudy/nightstudy.query";
+import { useMemo } from "react";
+import SkeletonComponent from "components/common/Skeleton";
 
 const NightStudyToday = () => {
   const [studentName, setStudentName] = useState("");
   const [nightStudyGrade, setNightStudyGrade] = useRecoilState(NightStudyGrade);
   const { NightStudyInfo } = useNightStudyStudentList();
   const [room, setRoom] = useRecoilState(PointSelectRoom);
+  const [isActive, setIsActive] = useState(true);
+  const { NightStudyProjectInfo } = useNightStudyProjectStudentsList();
+  const { data: projectData } = useGetNightStudyProjectStudents();
+
+  const projectNames = useMemo(() => {
+    if (!projectData?.data) return ["전체"];
+
+    const names = projectData.data.map((student) => student.projectName);
+    const unique = Array.from(new Set(names));
+    return ["전체", ...unique];
+  }, [projectData?.data]);
+
+
+  const [prname, setPrname] = useRecoilState(NightStudyProjectSelectAtom);
 
   return (
     <>
       <S.NightStudyHeaderContainer>
-        <S.SearchBarContainer>
-          <SearchBar value={studentName} onChange={setStudentName} />
-          <S.InfoText>
-            <span>*</span>심자 사유를 눌러 상세보기가 가능합니다.
-          </S.InfoText>
-        </S.SearchBarContainer>
-        <S.SelectContainer>
-          <Select
-            items={GRADE_ITEMS}
-            value={nightStudyGrade}
-            onChange={setNightStudyGrade}
-            zIndex={2}
-          />
-          <Select
-            items={["모든 학반", "1반", "2반", "3반", "4반"]}
-            value={room || "학반을 선택해주세요"}
-            onChange={setRoom}
-            zIndex={2}
-          />
-          <S.CsvButtonContainer>
-            <CsvButton
-              csvData={NightStudyInfo}
-              fileName={dayjs().format("YYYY-MM-DD") + "심자 중인 학생"}
+        <DodamSegmentedButton
+          width={220}
+          num={2}
+          type="inline"
+          data={[
+            { text: "개인", isAtv: isActive },
+            { text: "프로젝트", isAtv: !isActive },
+          ]}
+          onClick={() => {
+            setIsActive(!isActive);
+          }}
+        />
+        <div
+          style={{
+            display: "flex",
+          }}
+        >
+          <S.SearchBarContainer>
+            <SearchBar value={studentName} onChange={setStudentName} />
+          </S.SearchBarContainer>
+          <S.SelectContainer>
+            <Select
+              items={GRADE_ITEMS}
+              value={nightStudyGrade}
+              onChange={setNightStudyGrade}
+              zIndex={2}
             />
-          </S.CsvButtonContainer>
-        </S.SelectContainer>
-      </S.NightStudyHeaderContainer>
-      <TableAttribute
-        constant={NIGHTSTUDY_ALLOW_ITEMS}
-        thStyle={{ width: "14%" }}
-      >
-        <ErrorBoundary text="심자 중인 학생을 불러오지 못했습니다." showButton={true}>
-          <Suspense fallback={<>로딩중...</>}>
-            <NightStudyTodayItem
-              selectRoom={room}
-              studentName={studentName}
-              NightStudyGrade={changeGrade(nightStudyGrade)}
+            <Select
+              items={["모든 학반", "1반", "2반", "3반", "4반"]}
+              value={room || "학반을 선택해주세요"}
+              onChange={setRoom}
+              zIndex={2}
             />
-          </Suspense>
-        </ErrorBoundary>
-      </TableAttribute>
+            {!isActive && (
+              <Select
+                items={projectNames}
+                value={prname}
+                onChange={setPrname}
+                zIndex={4}
+              />
+            )}
+            <S.CsvButtonContainer>
+              {isActive ? (
+                <CsvButton
+                  csvData={NightStudyInfo}
+                  fileName={dayjs().format("YYYY-MM-DD") + "심자 중인 학생"}
+                />
+              ) : (
+                <CsvButton
+                  csvData={NightStudyProjectInfo}
+                  fileName={
+                    dayjs().format("YYYY-MM-DD") + "프로젝트 심자 중인 학생"}
+                />
+              )}
+            </S.CsvButtonContainer>
+          </S.SelectContainer>
+        </div>
+      </S.NightStudyHeaderContainer>
+      <S.InfoText>
+        <span>*</span>심자 사유를 눌러 상세보기가 가능합니다.
+      </S.InfoText>
+      {isActive ? (
+        <TableAttribute
+          constant={NIGHTSTUDY_ALLOW_ITEMS}
+          thStyle={{ width: "14%" }}
+        >
+          <ErrorBoundary
+            text="심자 중인 학생을 불러오지 못했습니다."
+            showButton={true}
+          >
+            <Suspense fallback={<SkeletonComponent height={80}/>}>
+              <NightStudyTodayItem
+                selectRoom={room}
+                studentName={studentName}
+                NightStudyGrade={changeGrade(nightStudyGrade)}
+              />
+            </Suspense>
+          </ErrorBoundary>
+        </TableAttribute>
+      ) : (
+        <TableAttribute constant={PROJECT_NIGHTSTUDY_STUDENTS_ITEM}>
+          <ErrorBoundary
+            text="심자 중인 학생을 불러오지 못했습니다."
+            showButton={true}
+          >
+            <Suspense fallback={<SkeletonComponent height={80}/>}>
+              <NightStudyProjectItem
+                selectRoom={room}
+                studentName={studentName}
+                NightStudyGrade={changeGrade(nightStudyGrade)}
+                projectName={prname}
+              />
+            </Suspense>
+          </ErrorBoundary>
+        </TableAttribute>
+      )}
     </>
   );
 };
diff --git a/src/components/NightStudy/NightStudyToday/style.ts b/src/components/NightStudy/NightStudyToday/style.ts
index 54f7b1fd..57330b79 100644
--- a/src/components/NightStudy/NightStudyToday/style.ts
+++ b/src/components/NightStudy/NightStudyToday/style.ts
@@ -1,29 +1,32 @@
-import styled from 'styled-components'
+import styled from "styled-components";
+import { DodamTypography} from "@b1nd/dds-web";
+
 
 export const NightStudyHeaderContainer = styled.div`
   display: flex;
   width: 100%;
   justify-content: space-between;
   align-items: center;
-`
+`;
 export const SearchBarContainer = styled.div`
   display: flex;
-`
+  margin-right: 10px;
+`;
 export const SelectContainer = styled.div`
   display: flex;
 
   gap: 10px;
-`
+`;
 export const InfoText = styled.p`
-  font-size: 12px;
-  color: red;
+  ${DodamTypography.Body2.Regular}
+  color: ${({ theme }) => theme.statusNegative};
 
   margin: 4px 0px 0px 10px;
-`
+`;
 
 export const CsvButtonContainer = styled.div`
   width: 100%;
   height: 60px;
   display: flex;
   justify-content: flex-end;
-`
+`;
diff --git a/src/constants/LateNight/latenight.constant.ts b/src/constants/LateNight/latenight.constant.ts
index a27654cb..a703ad26 100644
--- a/src/constants/LateNight/latenight.constant.ts
+++ b/src/constants/LateNight/latenight.constant.ts
@@ -20,6 +20,12 @@ export const PROJECT_NIGHTSTUDY_ALLOW_ITEMS = [
   "승인여부"
 ];
 
+export const PROJECT_NIGHTSTUDY_STUDENTS_ITEM = [
+  "프로젝트명",
+  "이름",
+  "학반",
+  "프로젝트 장소",
+]
 
 export const NIGHTSTUDY_BAN_TABLE_ITEMS = [
   "이름",
diff --git a/src/hooks/NightStudy/useNightStudyProjectStudentsList.ts b/src/hooks/NightStudy/useNightStudyProjectStudentsList.ts
new file mode 100644
index 00000000..055f30db
--- /dev/null
+++ b/src/hooks/NightStudy/useNightStudyProjectStudentsList.ts
@@ -0,0 +1,35 @@
+import { useGetNightStudyProjectStudents } from "queries/NightStudy/nightstudy.query"
+import { useEffect, useState } from "react"
+import { ProjectStudentType } from "types/NightStudy/nightstudy.type";
+
+export const useNightStudyProjectStudentsList = () =>{
+    const {data : ProjectStudents} = useGetNightStudyProjectStudents();
+    const [NightStudyProjectInfo, setNightStudyProjectInfo] = useState([
+        {
+            이름 : "",
+            반번호 : "",
+            프로젝트명 : "",
+            장소 : "",
+            심자체크 : "",
+            복귀체크: "",
+        }
+    ])
+
+    useEffect(()=>{
+        if(ProjectStudents?.data){
+            const newData = ProjectStudents.data.map((data : ProjectStudentType)=>({
+                이름 : data.name,
+                반번호 : data.number < 10
+                ? `${data.grade}${data.room}0${data.number}`
+                : `${data.grade}${data.room}${data.number}`,
+                프로젝트명 : data.projectName,
+                장소 : data.projectRoom,
+                심자체크 : "",
+                복귀체크 : "",
+            }))
+            setNightStudyProjectInfo(newData);
+        }
+    }, [ProjectStudents])
+
+    return {NightStudyProjectInfo}
+}
\ No newline at end of file
diff --git a/src/queries/NightStudy/nightstudy.query.ts b/src/queries/NightStudy/nightstudy.query.ts
index 68566f60..212340ef 100644
--- a/src/queries/NightStudy/nightstudy.query.ts
+++ b/src/queries/NightStudy/nightstudy.query.ts
@@ -1,5 +1,5 @@
 import { UseQueryOptions, useMutation, useQuery } from "react-query";
-import {ProjectNightStudyResponse } from "types/NightStudy/nightstudy.type";
+import {ProjectNightStudyResponse, ProjectStudentsResponse } from "types/NightStudy/nightstudy.type";
 import {AxiosError} from "axios";
 import {NightStudyBanResponse, NightStudyResponse, ProjectStudyDetailResponseType} from "types/NightStudy/nightstudy.type";
 import {QUERY_KEYS} from "../queryKey";
@@ -144,6 +144,17 @@ export const useGetNightStudyProjectDetail = (
   );
 };
 
+export const useGetNightStudyProjectStudents = (options?:UseQueryOptions<ProjectStudentsResponse, AxiosError,ProjectStudentsResponse,string>) => {
+  return useQuery(
+    QUERY_KEYS.nightstudy.getNightStudyProjectStudents,
+    ()=>nightstudyRepositoryImpl.getNightStudyProjectStudents(),
+    {
+      staleTime: 1000 * 60 * 60,
+      cacheTime: 1000 * 60 * 60,
+      ...options,
+    }
+  )
+}
 export const useDeleteNightStudyBan = () => {
   return useMutation((id: number) =>
     nightstudyRepositoryImpl.deleteNightStudyBan(id)
diff --git a/src/queries/queryKey.ts b/src/queries/queryKey.ts
index 92a83b4b..941d19f1 100644
--- a/src/queries/queryKey.ts
+++ b/src/queries/queryKey.ts
@@ -30,6 +30,7 @@ export const QUERY_KEYS = Object.freeze({
     getPendingNightStudy: '/night-study/pending',
     getNightStudyList: '/night-study',
     getPendingNightStudyProject : '/night-study/project/pending',
+    getNightStudyProjectStudents: '/night-study/project/students',
     getNightStudyBanMember: '/night-study/members',
     getNightStudyAllowedProjects : '/night-study/project/allowed',
     getNightStudyDetail : '/night-study/project/detail'
diff --git a/src/repositories/NightStudy/nightstudy.repository.ts b/src/repositories/NightStudy/nightstudy.repository.ts
index 4476ead5..b2ae8ccf 100644
--- a/src/repositories/NightStudy/nightstudy.repository.ts
+++ b/src/repositories/NightStudy/nightstudy.repository.ts
@@ -1,7 +1,8 @@
 import {
   NightStudyResponse,
   ProjectNightStudyResponse,
-  ProjectStudyDetailResponseType
+  ProjectStudyDetailResponseType,
+  ProjectStudentsResponse
 } from "types/NightStudy/nightstudy.type";
 
 export interface NightStudyRepository {
@@ -10,6 +11,7 @@ export interface NightStudyRepository {
   patchNightStudyAllow(id: number): Promise<void>;
   patchNightStudyCancel(id: number): Promise<void>;
   deleteNightStudyAllow(id: number): Promise<void>;
+  getNightStudyProjectStudents(): Promise<ProjectStudentsResponse>;
   getPendingNightStudyPending(): Promise<ProjectNightStudyResponse>;
   getNightStudyProjectDetail(id: number): Promise<ProjectStudyDetailResponseType>;
   patchNightStudyProjectAllow(id: number): Promise<void>;
diff --git a/src/repositories/NightStudy/nightstudy.repositoryImpl.ts b/src/repositories/NightStudy/nightstudy.repositoryImpl.ts
index ce192b75..10ff3110 100644
--- a/src/repositories/NightStudy/nightstudy.repositoryImpl.ts
+++ b/src/repositories/NightStudy/nightstudy.repositoryImpl.ts
@@ -2,6 +2,7 @@ import { dodamAxios } from "libs/Axios/customAxios";
 import {
   NightStudyResponse,
   ProjectNightStudyResponse,
+  ProjectStudentsResponse,
   ProjectStudyDetailResponseType,
 } from "types/NightStudy/nightstudy.type";
 import { NightStudyRepository } from "./nightstudy.repository";
@@ -48,6 +49,11 @@ class NightStudyRepositoryImpl implements NightStudyRepository {
     const { data } = await dodamAxios.get("/night-study/project/allowed");
     return data;
   }
+
+  public async getNightStudyProjectStudents(): Promise<ProjectStudentsResponse> {
+    const {data} = await dodamAxios.get("/night-study/project/students")
+    return data;
+  }
   public async getNightStudyProjectDetail(id: number): Promise<ProjectStudyDetailResponseType> {
     const { data } = await dodamAxios.get(`/night-study/project/${id}`);
     return data.data;
diff --git a/src/stores/NightStudy/nightstudy.store.ts b/src/stores/NightStudy/nightstudy.store.ts
index ea5005fc..0aac7943 100644
--- a/src/stores/NightStudy/nightstudy.store.ts
+++ b/src/stores/NightStudy/nightstudy.store.ts
@@ -35,3 +35,8 @@ export const NightStudyModalAtom = atom<number>({
   key: "NightStudyModalAtom",
   default: 0,
 })
+
+export const NightStudyProjectSelectAtom = atom<string>({
+  key : "NightStudyProjectSelectAtom",
+  default : "전체",
+})
\ No newline at end of file
diff --git a/src/types/NightStudy/nightstudy.type.ts b/src/types/NightStudy/nightstudy.type.ts
index 48793ece..b1329793 100644
--- a/src/types/NightStudy/nightstudy.type.ts
+++ b/src/types/NightStudy/nightstudy.type.ts
@@ -10,6 +10,10 @@ export interface ProjectNightStudyResponse extends Response {
   data: ProjectStudyType[];
 }
 
+export interface ProjectStudentsResponse extends Response {
+  data : ProjectStudentType[];
+}
+
 export interface NightStudyType {
   id: number;
   content: string;
@@ -32,6 +36,16 @@ export interface StudentType {
   number: number;
 }
 
+export interface ProjectStudentType {
+  id: number;
+  name: string;
+  grade: number;
+  room: number;
+  number: number;
+  projectName: string;
+  projectRoom: string;
+}
+
 export interface ProjectStudyType {
   id: number;
   type: "NIGHT_STUDY_PROJECT_1" | "NIGHT_STUDY_PROJECT_2";
diff --git a/src/utils/NightStudy/NightStudyStudentFillter.ts b/src/utils/NightStudy/NightStudyStudentFillter.ts
new file mode 100644
index 00000000..ea3adb89
--- /dev/null
+++ b/src/utils/NightStudy/NightStudyStudentFillter.ts
@@ -0,0 +1,34 @@
+import { ProjectStudentsResponse } from "types/NightStudy/nightstudy.type";
+import { changeRoom } from "utils/Member/changeGrade";
+
+export const NightStudyStudentFillter = (
+  NightStudyStudents: ProjectStudentsResponse| undefined,
+  studentName: string,
+  NightStudyGrade: number,
+  selectRoom: string,
+  selectedProject : string,
+) => {
+  return NightStudyStudents?.data
+    .filter((data) => data.name.includes(studentName))
+    .filter(
+      (data) => data.grade === NightStudyGrade || NightStudyGrade === 0
+    )
+
+    .filter(
+      (data) =>
+        data.room === changeRoom(selectRoom) ||
+        changeRoom(selectRoom) === 0
+    ) .filter(
+        (data) =>
+          selectedProject === "전체" || data.projectName === selectedProject
+      )
+    .sort((a, b) => {
+      if (a.grade === b.grade) {
+        if (a.room === b.room) {
+          return a.number - b.number;
+        }
+        return a.room - b.room;
+      }
+      return a.grade - b.grade;
+    });
+};


Reviews:
user: login: wjzlskxk
body: .csv to xlsx는 아직 안한건가요?
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T11:29:04Z
user: login: wjzlskxk
body: + Mac Numbers에서 연거말고excel에서 연견도 보여주세요.

state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T11:31:37Z
user: login: ftery0
body: ![image](https://github.com/user-attachments/assets/434a1945-c02d-49be-8c21-55fe48a9aca6)
이렇게 할거면 심자중인 학생 페이지에서 프로젝트, 개인 분리하는게 더 좋지 않을까
내가 사용할때 옆에 사이드바에 너무 많아서 불편하더라고 아예 개인심자랑 프로젝트를 같은 페이지에서 세그먼트 버튼으로 타입만 변경하는게 어떨까 싶어
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T11:36:51Z
user: login: dlskl
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T23:29:06Z
user: login: dlskl
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T23:29:35Z
user: login: dlskl
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T23:29:48Z

Review comments:
diff_hunk: @@ -0,0 +1,31 @@
+import type { CSSObject } from "styled-components";
+
+const trWrapStyles: CSSObject = {
+  overflow: "hidden",
+  textOverflow: "ellipsis",
+  whiteSpace: "nowrap"
+};
+
+export const TDProjectNameStyle: CSSObject = {
+  ...trWrapStyles,
+  textAlign: "left",
+  width: "180px"
+};
+
+export const TDNameStyle: CSSObject = {
+  ...trWrapStyles,
+  textAlign: "left",
+  width: "160px",
+};
+
+export const TDStudentStyle: CSSObject = {
+  ...trWrapStyles,
+  textAlign: "left",
+  width: "130px",
+};
+
+export const TDRoomStyle: CSSObject = {
+  ...trWrapStyles,
+  textAlign:"center",
+  width: "120px",
path: src/components/NightStudy/NightStudyToday/NightStudyProjectToday/style.ts
user: login: wjzlskxk
body: 코드 컨벤션 통일해주세요.

파스칼케이스로 다 바꾸던지, 카멜케이스로 다 바꾸던지..
created_at: 2025-05-13T11:02:35Z
author_association: MEMBER
diff_hunk: @@ -3,67 +3,144 @@ import * as S from "./style";
 import { Suspense, useState } from "react";
 import { GRADE_ITEMS } from "constants/Grade/grade.constant";
 import { useRecoilState } from "recoil";
-import { NightStudyGrade } from "stores/NightStudy/nightstudy.store";
+import { NightStudyGrade, NightStudyProjectSelectAtom } from "stores/NightStudy/nightstudy.store";
 import TableAttribute from "components/common/TableAttribute";
-import { NIGHTSTUDY_ALLOW_ITEMS } from "constants/LateNight/latenight.constant";
+import {
+  NIGHTSTUDY_ALLOW_ITEMS,
+  PROJECT_NIGHTSTUDY_STUDENTS_ITEM,
+} from "constants/LateNight/latenight.constant";
 import ErrorBoundary from "components/common/ErrorBoundary";
 import NightStudyTodayItem from "./NightStudyTodayItem";
 import { changeGrade } from "utils/Member/changeGrade";
 import CsvButton from "components/common/ExtractCsvData";
 import { useNightStudyStudentList } from "hooks/NightStudy/useNightStudyStudentList";
 import dayjs from "dayjs";
 import { PointSelectRoom } from "stores/Point/point.store";
+import { DodamSegmentedButton } from "@b1nd/dds-web";
+import NightStudyProjectItem from "./NightStudyProjectToday";
+import { useNightStudyProjectStudentsList } from "hooks/NightStudy/useNightStudyProjectStudentsList";
+import { useGetNightStudyProjectStudents } from "queries/NightStudy/nightstudy.query";
+import { useMemo } from "react";
 
 const NightStudyToday = () => {
   const [studentName, setStudentName] = useState("");
   const [nightStudyGrade, setNightStudyGrade] = useRecoilState(NightStudyGrade);
   const { NightStudyInfo } = useNightStudyStudentList();
   const [room, setRoom] = useRecoilState(PointSelectRoom);
+  const [isActive, setIsActive] = useState(true);
+  const { NightStudyProjectInfo } = useNightStudyProjectStudentsList();
+  const { data: projectData } = useGetNightStudyProjectStudents();
+
+  const projectNames = useMemo(() => {
+    if (!projectData?.data) return ["전체"];
+
+    const names = projectData.data.map((student) => student.projectName);
+    const unique = Array.from(new Set(names));
+    return ["전체", ...unique];
+  }, [projectData?.data]);
+
+
+  const [prname, setPrname] = useRecoilState(NightStudyProjectSelectAtom);
 
   return (
     <>
       <S.NightStudyHeaderContainer>
-        <S.SearchBarContainer>
-          <SearchBar value={studentName} onChange={setStudentName} />
-          <S.InfoText>
-            <span>*</span>심자 사유를 눌러 상세보기가 가능합니다.
-          </S.InfoText>
-        </S.SearchBarContainer>
-        <S.SelectContainer>
-          <Select
-            items={GRADE_ITEMS}
-            value={nightStudyGrade}
-            onChange={setNightStudyGrade}
-            zIndex={2}
-          />
-          <Select
-            items={["모든 학반", "1반", "2반", "3반", "4반"]}
-            value={room || "학반을 선택해주세요"}
-            onChange={setRoom}
-            zIndex={2}
-          />
-          <S.CsvButtonContainer>
-            <CsvButton
-              csvData={NightStudyInfo}
-              fileName={dayjs().format("YYYY-MM-DD") + "심자 중인 학생"}
+        <DodamSegmentedButton
+          width={220}
+          num={2}
+          type="inline"
+          data={[
+            { text: "개인", isAtv: isActive },
+            { text: "프로젝트", isAtv: !isActive },
+          ]}
+          onClick={() => {
+            setIsActive(!isActive);
+          }}
+        />
+        <div
+          style={{
+            display: "flex",
+          }}
+        >
+          <S.SearchBarContainer>
+            <SearchBar value={studentName} onChange={setStudentName} />
+          </S.SearchBarContainer>
+          <S.SelectContainer>
+            <Select
+              items={GRADE_ITEMS}
+              value={nightStudyGrade}
+              onChange={setNightStudyGrade}
+              zIndex={2}
             />
-          </S.CsvButtonContainer>
-        </S.SelectContainer>
-      </S.NightStudyHeaderContainer>
-      <TableAttribute
-        constant={NIGHTSTUDY_ALLOW_ITEMS}
-        thStyle={{ width: "14%" }}
-      >
-        <ErrorBoundary text="심자 중인 학생을 불러오지 못했습니다." showButton={true}>
-          <Suspense fallback={<>로딩중...</>}>
-            <NightStudyTodayItem
-              selectRoom={room}
-              studentName={studentName}
-              NightStudyGrade={changeGrade(nightStudyGrade)}
+            <Select
+              items={["모든 학반", "1반", "2반", "3반", "4반"]}
+              value={room || "학반을 선택해주세요"}
+              onChange={setRoom}
+              zIndex={2}
             />
-          </Suspense>
-        </ErrorBoundary>
-      </TableAttribute>
+            {!isActive && (
+              <Select
+                items={projectNames}
+                value={prname}
+                onChange={setPrname}
+                zIndex={4}
+              />
+            )}
+            <S.CsvButtonContainer>
+              {isActive ? (
+                <CsvButton
+                  csvData={NightStudyInfo}
+                  fileName={dayjs().format("YYYY-MM-DD") + "심자 중인 학생"}
+                />
+              ) : (
+                <CsvButton
+                  csvData={NightStudyProjectInfo}
+                  fileName={
+                    dayjs().format("YYYY-MM-DD") + "프로젝트 심자 중인 학생"}
+                />
+              )}
+            </S.CsvButtonContainer>
+          </S.SelectContainer>
+        </div>
+      </S.NightStudyHeaderContainer>
+      <S.InfoText>
+        <span>*</span>심자 사유를 눌러 상세보기가 가능합니다.
+      </S.InfoText>
+      {isActive ? (
+        <TableAttribute
+          constant={NIGHTSTUDY_ALLOW_ITEMS}
+          thStyle={{ width: "14%" }}
+        >
+          <ErrorBoundary
+            text="심자 중인 학생을 불러오지 못했습니다."
+            showButton={true}
+          >
+            <Suspense fallback={<>로딩중...</>}>
path: src/components/NightStudy/NightStudyToday/index.tsx
user: login: ftery0
body: 스켈레톤 ui 쓰는것도 좋을듯
created_at: 2025-05-13T11:26:21Z
author_association: MEMBER
diff_hunk: @@ -1,29 +1,30 @@
-import styled from 'styled-components'
+import styled from "styled-components";
 
 export const NightStudyHeaderContainer = styled.div`
   display: flex;
   width: 100%;
   justify-content: space-between;
   align-items: center;
-`
+`;
 export const SearchBarContainer = styled.div`
   display: flex;
-`
+  margin-right: 10px;
+`;
 export const SelectContainer = styled.div`
   display: flex;
 
   gap: 10px;
-`
+`;
 export const InfoText = styled.p`
   font-size: 12px;
path: src/components/NightStudy/NightStudyToday/style.ts
user: login: ftery0
body: dds 사용해야지??
created_at: 2025-05-13T11:26:40Z
author_association: MEMBER
diff_hunk: @@ -1,29 +1,30 @@
-import styled from 'styled-components'
+import styled from "styled-components";
 
 export const NightStudyHeaderContainer = styled.div`
   display: flex;
   width: 100%;
   justify-content: space-between;
   align-items: center;
-`
+`;
 export const SearchBarContainer = styled.div`
   display: flex;
-`
+  margin-right: 10px;
+`;
 export const SelectContainer = styled.div`
   display: flex;
 
   gap: 10px;
-`
+`;
 export const InfoText = styled.p`
   font-size: 12px;
path: src/components/NightStudy/NightStudyToday/style.ts
user: login: ftery0
body: 그리고 이런거는 상속을 사용하여 하는게 좋아보임
created_at: 2025-05-13T11:27:31Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,31 @@
+import type { CSSObject } from "styled-components";
+
+const trWrapStyles: CSSObject = {
+  overflow: "hidden",
+  textOverflow: "ellipsis",
+  whiteSpace: "nowrap"
+};
+
+export const TDProjectNameStyle: CSSObject = {
+  ...trWrapStyles,
+  textAlign: "left",
+  width: "180px"
+};
+
+export const TDNameStyle: CSSObject = {
+  ...trWrapStyles,
+  textAlign: "left",
+  width: "160px",
+};
+
+export const TDStudentStyle: CSSObject = {
+  ...trWrapStyles,
+  textAlign: "left",
+  width: "130px",
+};
+
+export const TDRoomStyle: CSSObject = {
+  ...trWrapStyles,
+  textAlign:"center",
+  width: "120px",
path: src/components/NightStudy/NightStudyToday/NightStudyProjectToday/style.ts
user: login: dlskl
body: 네 알겠습니다. 수정하겠습니다. 프로젝트 심자 때 쓸 엑셀이 필요하다고 들어서 일단 csv 형식으로 개발하였고, 후에 xlsl 형식으로 개인 심자와 프로젝트 심자를 개발할 예정입니다
created_at: 2025-05-13T23:29:06Z
author_association: MEMBER
diff_hunk: @@ -1,29 +1,30 @@
-import styled from 'styled-components'
+import styled from "styled-components";
 
 export const NightStudyHeaderContainer = styled.div`
   display: flex;
   width: 100%;
   justify-content: space-between;
   align-items: center;
-`
+`;
 export const SearchBarContainer = styled.div`
   display: flex;
-`
+  margin-right: 10px;
+`;
 export const SelectContainer = styled.div`
   display: flex;
 
   gap: 10px;
-`
+`;
 export const InfoText = styled.p`
   font-size: 12px;
path: src/components/NightStudy/NightStudyToday/style.ts
user: login: dlskl
body: 넵 수정하겠습니다.
created_at: 2025-05-13T23:29:35Z
author_association: MEMBER
diff_hunk: @@ -3,67 +3,144 @@ import * as S from "./style";
 import { Suspense, useState } from "react";
 import { GRADE_ITEMS } from "constants/Grade/grade.constant";
 import { useRecoilState } from "recoil";
-import { NightStudyGrade } from "stores/NightStudy/nightstudy.store";
+import { NightStudyGrade, NightStudyProjectSelectAtom } from "stores/NightStudy/nightstudy.store";
 import TableAttribute from "components/common/TableAttribute";
-import { NIGHTSTUDY_ALLOW_ITEMS } from "constants/LateNight/latenight.constant";
+import {
+  NIGHTSTUDY_ALLOW_ITEMS,
+  PROJECT_NIGHTSTUDY_STUDENTS_ITEM,
+} from "constants/LateNight/latenight.constant";
 import ErrorBoundary from "components/common/ErrorBoundary";
 import NightStudyTodayItem from "./NightStudyTodayItem";
 import { changeGrade } from "utils/Member/changeGrade";
 import CsvButton from "components/common/ExtractCsvData";
 import { useNightStudyStudentList } from "hooks/NightStudy/useNightStudyStudentList";
 import dayjs from "dayjs";
 import { PointSelectRoom } from "stores/Point/point.store";
+import { DodamSegmentedButton } from "@b1nd/dds-web";
+import NightStudyProjectItem from "./NightStudyProjectToday";
+import { useNightStudyProjectStudentsList } from "hooks/NightStudy/useNightStudyProjectStudentsList";
+import { useGetNightStudyProjectStudents } from "queries/NightStudy/nightstudy.query";
+import { useMemo } from "react";
 
 const NightStudyToday = () => {
   const [studentName, setStudentName] = useState("");
   const [nightStudyGrade, setNightStudyGrade] = useRecoilState(NightStudyGrade);
   const { NightStudyInfo } = useNightStudyStudentList();
   const [room, setRoom] = useRecoilState(PointSelectRoom);
+  const [isActive, setIsActive] = useState(true);
+  const { NightStudyProjectInfo } = useNightStudyProjectStudentsList();
+  const { data: projectData } = useGetNightStudyProjectStudents();
+
+  const projectNames = useMemo(() => {
+    if (!projectData?.data) return ["전체"];
+
+    const names = projectData.data.map((student) => student.projectName);
+    const unique = Array.from(new Set(names));
+    return ["전체", ...unique];
+  }, [projectData?.data]);
+
+
+  const [prname, setPrname] = useRecoilState(NightStudyProjectSelectAtom);
 
   return (
     <>
       <S.NightStudyHeaderContainer>
-        <S.SearchBarContainer>
-          <SearchBar value={studentName} onChange={setStudentName} />
-          <S.InfoText>
-            <span>*</span>심자 사유를 눌러 상세보기가 가능합니다.
-          </S.InfoText>
-        </S.SearchBarContainer>
-        <S.SelectContainer>
-          <Select
-            items={GRADE_ITEMS}
-            value={nightStudyGrade}
-            onChange={setNightStudyGrade}
-            zIndex={2}
-          />
-          <Select
-            items={["모든 학반", "1반", "2반", "3반", "4반"]}
-            value={room || "학반을 선택해주세요"}
-            onChange={setRoom}
-            zIndex={2}
-          />
-          <S.CsvButtonContainer>
-            <CsvButton
-              csvData={NightStudyInfo}
-              fileName={dayjs().format("YYYY-MM-DD") + "심자 중인 학생"}
+        <DodamSegmentedButton
+          width={220}
+          num={2}
+          type="inline"
+          data={[
+            { text: "개인", isAtv: isActive },
+            { text: "프로젝트", isAtv: !isActive },
+          ]}
+          onClick={() => {
+            setIsActive(!isActive);
+          }}
+        />
+        <div
+          style={{
+            display: "flex",
+          }}
+        >
+          <S.SearchBarContainer>
+            <SearchBar value={studentName} onChange={setStudentName} />
+          </S.SearchBarContainer>
+          <S.SelectContainer>
+            <Select
+              items={GRADE_ITEMS}
+              value={nightStudyGrade}
+              onChange={setNightStudyGrade}
+              zIndex={2}
             />
-          </S.CsvButtonContainer>
-        </S.SelectContainer>
-      </S.NightStudyHeaderContainer>
-      <TableAttribute
-        constant={NIGHTSTUDY_ALLOW_ITEMS}
-        thStyle={{ width: "14%" }}
-      >
-        <ErrorBoundary text="심자 중인 학생을 불러오지 못했습니다." showButton={true}>
-          <Suspense fallback={<>로딩중...</>}>
-            <NightStudyTodayItem
-              selectRoom={room}
-              studentName={studentName}
-              NightStudyGrade={changeGrade(nightStudyGrade)}
+            <Select
+              items={["모든 학반", "1반", "2반", "3반", "4반"]}
+              value={room || "학반을 선택해주세요"}
+              onChange={setRoom}
+              zIndex={2}
             />
-          </Suspense>
-        </ErrorBoundary>
-      </TableAttribute>
+            {!isActive && (
+              <Select
+                items={projectNames}
+                value={prname}
+                onChange={setPrname}
+                zIndex={4}
+              />
+            )}
+            <S.CsvButtonContainer>
+              {isActive ? (
+                <CsvButton
+                  csvData={NightStudyInfo}
+                  fileName={dayjs().format("YYYY-MM-DD") + "심자 중인 학생"}
+                />
+              ) : (
+                <CsvButton
+                  csvData={NightStudyProjectInfo}
+                  fileName={
+                    dayjs().format("YYYY-MM-DD") + "프로젝트 심자 중인 학생"}
+                />
+              )}
+            </S.CsvButtonContainer>
+          </S.SelectContainer>
+        </div>
+      </S.NightStudyHeaderContainer>
+      <S.InfoText>
+        <span>*</span>심자 사유를 눌러 상세보기가 가능합니다.
+      </S.InfoText>
+      {isActive ? (
+        <TableAttribute
+          constant={NIGHTSTUDY_ALLOW_ITEMS}
+          thStyle={{ width: "14%" }}
+        >
+          <ErrorBoundary
+            text="심자 중인 학생을 불러오지 못했습니다."
+            showButton={true}
+          >
+            <Suspense fallback={<>로딩중...</>}>
path: src/components/NightStudy/NightStudyToday/index.tsx
user: login: dlskl
body: 수정해보도록 하겠습니다.
created_at: 2025-05-13T23:29:48Z
author_association: MEMBER

PR comments:
user: login: dlskl
created_at: 2025-05-13T23:32:37Z
body: @ftery0 네 선배님 혹시 예를 들자면 심자 신청받을 때 세그먼트 버튼을 사용해서 개인 or 프로젝트 이렇게 타입만 변경하여 신청 받을 수 있게 말씀하시는 건가요?
author_association: MEMBER
user: login: ftery0
created_at: 2025-05-13T23:34:47Z
body: 응 그렇게 하는게 좋아 보임 아니면 피그마에서 디자인을 먼저 해봐
author_association: MEMBER
user: login: dlskl
created_at: 2025-05-13T23:43:08Z
body: @ftery0 넵 피그마에서 디자인을 한 뒤에 수정하여 개발해보도록 하겠습니다
author_association: MEMBER
user: login: dlskl
created_at: 2025-05-13T23:44:22Z
body: @wjzlskxk 
<img width="448" alt="스크린샷 2025-05-14 오전 8 39 44" src="https://github.com/user-attachments/assets/98763e67-f4ee-4ec5-b59f-482954dd0a2b" />
<img width="313" alt="스크린샷 2025-05-14 오전 8 40 32" src="https://github.com/user-attachments/assets/73f0911d-1521-43d9-9bab-5ae66625f755" />
처음에 csv 파일을 엑셀에서 열 때 텍스트 마법사에서 세팅을 하면 위에 있는 사진처럼 나오고 텍스트 마법사에서 세팅을 안할 시에는 밑에 있는 사진처럼 나옵니다.
author_association: MEMBER
user: login: wjzlskxk
created_at: 2025-05-13T23:59:58Z
body: > @wjzlskxk <img alt="스크린샷 2025-05-14 오전 8 39 44" width="448" src="https://private-user-images.githubusercontent.com/165901278/443430296-98763e67-f4ee-4ec5-b59f-482954dd0a2b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDcxODAxNjQsIm5iZiI6MTc0NzE3OTg2NCwicGF0aCI6Ii8xNjU5MDEyNzgvNDQzNDMwMjk2LTk4NzYzZTY3LWY0ZWUtNGVjNS1iNTlmLTQ4Mjk1NGRkMGEyYi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNTEzJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDUxM1QyMzQ0MjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wMzY3NDMxZDc1YTdkY2JlYWU0OWY5M2JiNmUyNTRmZDdmNmQyNTY5MmM1ZDgwY2EzZWQ3MmU1ODI3YWEyM2EwJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.iZC9JwMios-GA8tUsDPGC1V7up53R52XN0UzLnvivbM"> <img alt="스크린샷 2025-05-14 오전 8 40 32" width="313" src="https://private-user-images.githubusercontent.com/165901278/443430302-73f0911d-1521-43d9-9bab-5ae66625f755.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDcxODAxNjQsIm5iZiI6MTc0NzE3OTg2NCwicGF0aCI6Ii8xNjU5MDEyNzgvNDQzNDMwMzAyLTczZjA5MTFkLTE1MjEtNDNkOS05YmFiLTVhZTY2NjI1Zjc1NS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNTEzJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDUxM1QyMzQ0MjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1kMzAwZTg3ODIwMzljODVkZjA0ZDgzNGNkZGVkMGQzZjg0N2I3NTQ3MmExMjdiZTk2ZDBiZTQ5N2FlMDdmYWYxJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.CC7ESPflQQSr3SWnjuis_IRLEfZ-ZFxrczpeIUrciuc"> 처음에 텍스트 마법사에서 세팅을 하면 위에 있는 사진처럼 나오고 텍스트 마법사에서 세팅을 안할 시에는 밑에 있는 사진처럼 나옵니다.

이 사진 보니까 셀 사이에 보더도 없는데 react-csv에서 exceljs로 바꾸는거도 나중에 같이 할 예정인가요?
author_association: MEMBER
user: login: dlskl
created_at: 2025-05-14T00:12:31Z
body: > > @wjzlskxk <img alt="스크린샷 2025-05-14 오전 8 39 44" width="448" src="https://private-user-images.githubusercontent.com/165901278/443430296-98763e67-f4ee-4ec5-b59f-482954dd0a2b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDcxODAxNjQsIm5iZiI6MTc0NzE3OTg2NCwicGF0aCI6Ii8xNjU5MDEyNzgvNDQzNDMwMjk2LTk4NzYzZTY3LWY0ZWUtNGVjNS1iNTlmLTQ4Mjk1NGRkMGEyYi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNTEzJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDUxM1QyMzQ0MjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wMzY3NDMxZDc1YTdkY2JlYWU0OWY5M2JiNmUyNTRmZDdmNmQyNTY5MmM1ZDgwY2EzZWQ3MmU1ODI3YWEyM2EwJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.iZC9JwMios-GA8tUsDPGC1V7up53R52XN0UzLnvivbM"> <img alt="스크린샷 2025-05-14 오전 8 40 32" width="313" src="https://private-user-images.githubusercontent.com/165901278/443430302-73f0911d-1521-43d9-9bab-5ae66625f755.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDcxODAxNjQsIm5iZiI6MTc0NzE3OTg2NCwicGF0aCI6Ii8xNjU5MDEyNzgvNDQzNDMwMzAyLTczZjA5MTFkLTE1MjEtNDNkOS05YmFiLTVhZTY2NjI1Zjc1NS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNTEzJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDUxM1QyMzQ0MjRaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1kMzAwZTg3ODIwMzljODVkZjA0ZDgzNGNkZGVkMGQzZjg0N2I3NTQ3MmExMjdiZTk2ZDBiZTQ5N2FlMDdmYWYxJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.CC7ESPflQQSr3SWnjuis_IRLEfZ-ZFxrczpeIUrciuc"> 처음에 텍스트 마법사에서 세팅을 하면 위에 있는 사진처럼 나오고 텍스트 마법사에서 세팅을 안할 시에는 밑에 있는 사진처럼 나옵니다.
> 
> 이 사진 보니까 셀 사이에 보더도 없는데 react-csv에서 exceljs로 바꾸는거도 나중에 같이 할 예정인가요?

 네 그렇게 할 예정입니다
author_association: MEMBER