Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
1736620
feat: 트랜잭션 성능 테스트 코드 작성
twalla26 Dec 5, 2024
837a8a4
chore: k6 설치
twalla26 Dec 5, 2024
4216d75
fix: 비디오 미리보기 카메라끄고 시작해도 카메라 인디케이터가 켜져있던 문제 해결
ShipFriend0516 Dec 5, 2024
c82d76f
refactor: 질문 삭제 로직을 테스트 가능하도록 수정
twalla26 Dec 5, 2024
018938a
style: 사이드바에 로고 추가 및 사이드바 크기 변경, 색상 조정
yiseungyun Dec 5, 2024
a40327d
style: 질문지 탭, 채널 탭 스타일 통일
yiseungyun Dec 5, 2024
331ed59
Merge pull request #333 from twalla26/refactor/test
blu3fishez Dec 5, 2024
16c703e
style: 질문지 페이지, 스터디 채널 페이지 카드 애니메이션 통일
yiseungyun Dec 5, 2024
082dc69
style: 질문지, 채널 페이지 카테고리 글자 잘림 해결
yiseungyun Dec 5, 2024
02b1801
style: 질문지, 채널 페이지 카드 테두리 통일
yiseungyun Dec 5, 2024
20c5ce4
Merge branch 'dev' of https://github.com/boostcampwm-2024/web27-Previ…
yiseungyun Dec 5, 2024
9253c27
style: 16이 아닌 sidebar로 이름 변경
yiseungyun Dec 5, 2024
903a851
fix: 비디오 끄고 참가시, 비디오 스트림 연결이 되지 않던 오류 해결
ShipFriend0516 Dec 5, 2024
ecc388e
fix: 세션 외부에서도 navigate blocking이 되던 문제 해결
ShipFriend0516 Dec 5, 2024
1826198
feat: 데이터채널 생성시 즉시 상태 업데이트하는 메시지 발송
ShipFriend0516 Dec 5, 2024
51b936b
Merge pull request #334 from boostcampwm-2024/refactor/fe
ShipFriend0516 Dec 5, 2024
0634530
Merge pull request #335 from ShipFriend0516/fix/media-preview
ShipFriend0516 Dec 5, 2024
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
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@nestjs/typeorm": "^10.0.2",
"@nestjs/websockets": "^10.4.6",
"@socket.io/redis-adapter": "^8.3.0",
"@types/k6": "^0.54.2",
"@types/passport-jwt": "^4.0.1",
"axios": "^1.7.7",
"class-transformer": "^0.5.1",
Expand Down
48 changes: 19 additions & 29 deletions backend/src/question-list/question-list.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { DataSource, In, SelectQueryBuilder } from "typeorm";
import { PaginateMetaDto } from "@/question-list/dto/paginate-meta.dto";
import { PaginateDto } from "@/question-list/dto/paginate.dto";
import { QuestionListDto } from "@/question-list/dto/question-list.dto";
import { Transactional } from "typeorm-transactional";

@Injectable()
export class QuestionListService {
Expand Down Expand Up @@ -71,6 +72,7 @@ export class QuestionListService {
}

// 질문 생성 메서드
@Transactional()
async createQuestionList(createQuestionListDto: CreateQuestionListDto) {
const { title, contents, categoryNames, isPublic, userId } = createQuestionListDto;

Expand All @@ -81,37 +83,24 @@ export class QuestionListService {
throw new Error("Some category names were not found.");
}

const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

try {
const questionList = new QuestionList();
questionList.title = title;
questionList.categories = categories;
questionList.isPublic = isPublic;
questionList.userId = userId;

const createdQuestionList = await queryRunner.manager.save(questionList);
const questionList = new QuestionList();
questionList.title = title;
questionList.categories = categories;
questionList.isPublic = isPublic;
questionList.userId = userId;

const questions = contents.map((content, index) => {
const question = new Question();
question.content = content;
question.index = index;
question.questionList = createdQuestionList;
const createdQuestionList = await this.questionListRepository.save(questionList);

return question;
});
const createdQuestions = await queryRunner.manager.save(questions);
const questions = contents.map((content, index) => {
const question = new Question();
question.content = content;
question.index = index;
question.questionList = createdQuestionList;

await queryRunner.commitTransaction();
return { createdQuestionList, createdQuestions };
} catch (error) {
await queryRunner.rollbackTransaction();
throw new Error(error.message);
} finally {
await queryRunner.release();
}
return question;
});
const createdQuestions = await this.questionRepository.save(questions);
return { createdQuestionList, createdQuestions };
}

async getQuestionListContents(questionListId: number, userId: number) {
Expand Down Expand Up @@ -272,7 +261,8 @@ export class QuestionListService {
}

async deleteQuestion(deleteQuestionDto: DeleteQuestionDto) {
const { id, questionListId, userId } = deleteQuestionDto;
const { id, userId } = deleteQuestionDto;
const questionListId = await this.questionRepository.getQuestionListIdByQuestionId(id);

const question = await this.questionRepository.findOne({
where: { id },
Expand Down
9 changes: 9 additions & 0 deletions backend/src/question-list/repository/question.respository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,13 @@ export class QuestionRepository extends Repository<Question> {
.orderBy("question.index", "ASC")
.getMany();
}

async getQuestionListIdByQuestionId(questionId: number) {
const result = await this.createQueryBuilder("question")
.select("question.questionListId")
.where("question.id = :questionId", { questionId })
.getOne();

return result ? result.questionListId : null;
}
}
47 changes: 47 additions & 0 deletions backend/test/k6/create-question-list-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import http from "k6/http";
import { check, sleep } from "k6";

export const options = {
vus: 100, // 108명의 사용자
duration: "5s", // 5초 동안 동시 요청을 실행
};

const BASE_URL = "http://localhost:3000";
const COOKIE =
"accessToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiY2FtcGVyXzEwMTYzODkyMyIsImxvZ2luVHlwZSI6ImxvY2FsIiwiaWF0IjoxNzMzMzI2NTIzfQ.OA9o0twIqKUNdTJhnHZkSKytoUvp052VsywKdYvSn30; refreshToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzQxOTA1MjM5MDgsImlhdCI6MTczMzMyNjUyMywiYXVkIjoiMSJ9.ZXYCwUL8EOjc1xJ3BJ_bHCLQHa20_qxf1FYqolgxP4I";

const questionListData = {
title: "Sample Question List for Test",
contents: [
"This is 1st question!",
"This is 2nd question!",
"This is 3rd question!",
"This is 4th question!",
"This is 5th question!",
"This is 6th question!",
"This is 7th question!",
"This is 8th question!",
"This is 9th question!",
"This is 10th question!",
],
categoryNames: ["보안", "네트워크", "자료구조"],
isPublic: true,
};

export default function () {
const url = `${BASE_URL}/question-list`;
const params = {
headers: {
Cookie: `${COOKIE}`,
"Content-Type": "application/json",
},
};

const response = http.post(url, JSON.stringify(questionListData), params);

check(response, {
"is status 200": (r) => r.status === 200,
});

sleep(1);
}
32 changes: 32 additions & 0 deletions backend/test/k6/delete-question-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import http from "k6/http";
import { check, sleep } from "k6";

export const options = {
vus: 100, // 100명의 사용자
duration: "5s", // 5초 동안 동시 요청을 실행
};

const BASE_URL = "http://localhost:3000";
const COOKIE =
"accessToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiY2FtcGVyXzEwMTYzODkyMyIsImxvZ2luVHlwZSI6ImxvY2FsIiwiaWF0IjoxNzMzMzI2NTIzfQ.OA9o0twIqKUNdTJhnHZkSKytoUvp052VsywKdYvSn30; refreshToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzQxOTA1MjM5MDgsImlhdCI6MTczMzMyNjUyMywiYXVkIjoiMSJ9.ZXYCwUL8EOjc1xJ3BJ_bHCLQHa20_qxf1FYqolgxP4I";

export default function () {
const questionListId = 27;
const questionId = 66176 + (__VU - 1) * 10 + __ITER;
console.log(questionId);

const url = `${BASE_URL}/question-list/${questionListId}/question/${questionId}`;
const params = {
headers: {
Cookie: `${COOKIE}`,
},
};

const response = http.del(url, null, params);

check(response, {
"is status 200": (r) => r.status === 200,
});

sleep(1);
}
Binary file added frontend/public/preview-logo2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions frontend/src/components/common/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const Sidebar = () => {
return (
<nav
className={
"min-w-17.5 w-17.5 h-screen flex flex-col border-r-custom-s gap-1.5 justify-between overflow-y-hidden bg-white transition-colors dark:bg-gray-black dark:border-r-gray-400"
"min-w-sidebar w-sidebar h-screen flex flex-col border-r-custom-s gap-1.5 justify-between overflow-y-hidden bg-white transition-colors dark:bg-gray-black dark:border-r-gray-400"
}
>
<div>
Expand All @@ -45,9 +45,9 @@ const Sidebar = () => {
"text-green-400 text-5xl text-center py-7 font-bold hover:tracking-widest transition-all duration-700 font-raleway dark:text-green-100"
}
>
Preview
<img className="w-full px-9" src="/preview-logo2.png" alt="Preview 로고" />
</header>
<hr className={"mx-4 dark:border-gray-400"} />
<hr className={"mx-3 dark:border-gray-400"} />
<ul
className={"flex flex-col gap-2 items-center mx-2 my-2 p-2"}
aria-label={"사이드바 링크 리스트"}
Expand All @@ -63,9 +63,9 @@ const Sidebar = () => {
onClick={
route.path
? () =>
navigate(route.path!, {
state: { from: route.path ?? "/" },
})
navigate(route.path!, {
state: { from: route.path ?? "/" },
})
: route.onClick
}
/>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/common/Sidebar/SidebarMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const SidebarMenu = ({
onClick,
}: SidebarMenuProps) => {
const activeClass = isSelected
? "bg-green-100 dark:text-gray-black text-white text-semibold-m"
? "bg-[#53D187] dark:text-gray-black text-white text-semibold-m" // TODO: 나중에 색상 시스템 전체적으로 밝게 업데이트 예정
: "bg-transparent dark:text-white text-gray-black text-medium-l transition-color duration-300 hover:bg-gray-200/30";

return (
Expand Down
11 changes: 5 additions & 6 deletions frontend/src/components/questions/QuestionsPreviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ const QuestionCard = ({
return (
<Link
to={`/questions/${id}`}
className="bg-white backdrop-blur-sm border border-gray-200 rounded-xl p-4 hover:bg-gray-200/70 transition-all cursor-pointer group hover:border-green-200 dark:bg-gray-900/80 dark:border-gray-700 dark:hover:bg-gray-600/70 hover:shadow-lg"
className="bg-white backdrop-blur-sm border border-gray-200 rounded-xl p-4 hover:bg-gray-200/70 cursor-pointer group duration-200 ease-in-out hover:-translate-y-1.5 hover:border-green-200 dark:bg-gray-900/80 dark:border-gray-700 dark:hover:bg-gray-600/70 hover:shadow-16"
>
<div className="flex justify-between items-start mb-3 pt-1">
<span className="px-3 py-0.5 bg-emerald-50 text-emerald-600 dark:bg-emerald-600/20 dark:text-emerald-400 text-sm rounded-full">
{category}
</span>
<FaStar
className={`w-5 h-5 ${
isStarred
? "text-yellow-400 fill-yellow-400"
: "text-gray-500 group-hover:text-gray-400"
}`}
className={`w-5 h-5 ${isStarred
? "text-yellow-400 fill-yellow-400"
: "text-gray-500 group-hover:text-gray-400"
}`}
/>
</div>
<h3 className="text-lg font-semibold text-black dark:text-white pl-1 mb-4 line-clamp-2">
Expand Down
7 changes: 3 additions & 4 deletions frontend/src/components/session/MediaPreviewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ const MediaPreviewModal = ({
const getMediaPreview = useCallback(async () => {
const mediaStream = await getMediaStream("video");
if (!mediaStream) {
toast.error(
"비디오 장치를 찾을 수 없습니다. 비디오 장치 없이 세션에 참가합니다."
);
toast.error("비디오 장치를 찾을 수 없습니다.");
}
setPreview(mediaStream);
}, []);
Expand Down Expand Up @@ -100,7 +98,7 @@ const MediaPreviewModal = ({
className={"w-6 h-6"}
type={"checkbox"}
title={"dd"}
onClick={() => setIsVideoOn(!isVideoOn)}
onChange={() => setIsVideoOn(!isVideoOn)}
/>
<span>내 비디오 끄고 참가하기</span>
</label>
Expand Down Expand Up @@ -134,6 +132,7 @@ const MediaPreviewModal = ({
onConfirm();
setReady(true);
modal.closeModal();
preview?.getTracks().forEach((track) => track.stop());
}}
className={
"rounded-custom-m px-16 py-4 bg-green-500 text-white hover:bg-green-600"
Expand Down
12 changes: 7 additions & 5 deletions frontend/src/pages/QuestionListPage/view/QuestionListHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ const QuestionListHeader = ({
질문지 리스트
</h1>
<div className="h-11 flex gap-2 items-stretch justify-between">
<CategorySelect
value={selectedCategory}
setValue={setSelectedCategory}
options={options}
/>
<div className="w-36">
<CategorySelect
value={selectedCategory}
setValue={setSelectedCategory}
options={options}
/>
</div>
<SearchBar text={"질문지 검색하기"} />
</div>
<Tabs tab={tab} setTab={setTab} />
Expand Down
7 changes: 3 additions & 4 deletions frontend/src/pages/QuestionListPage/view/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const Tabs = ({ tab, setTab }: TabsProps) => {

return (
<div
className={"relative border-b mt-4 pt-4 py-2 flex gap-4 justify-between "}
className={"relative border-b mt-6 pt-4 py-2 flex gap-4 justify-between "}
>
<div className={"flex gap-4 "}>
{tabs.map(
Expand All @@ -29,9 +29,8 @@ const Tabs = ({ tab, setTab }: TabsProps) => {
<button
key={tab.value}
onClick={() => setTab(tab.value as Tab)}
className={`w-28 h-full ${
selectedTab === index ? selectedClassName : ""
}`}
className={`w-28 h-full ${selectedTab === index ? selectedClassName : ""
}`}
>
{tab.name}
</button>
Expand Down
14 changes: 8 additions & 6 deletions frontend/src/pages/SessionListPage/SessionListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,17 @@ const SessionListPage = () => {
<div className="">
<h1 className={"text-bold-l mb-6"}>스터디 채널</h1>
<div className={"h-11 flex gap-2 w-full"}>
<CategorySelect
value={"FE"}
setValue={setSelectedCategory}
options={options}
/>
<div className="w-36">
<CategorySelect
value={"FE"}
setValue={setSelectedCategory}
options={options}
/>
</div>
<SearchBar text="세션을 검색하세요" />
</div>
</div>
<div className="relative flex justify-between mt-4 pt-4 mb-12 ">
<div className="relative flex justify-between mt-4 pt-4 mb-12 ">
<TabContainer currentTab={currentTab} setCurrentTab={setCurrentTab} />
<Link
to="/sessions/create"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/SessionListPage/view/SessionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const SessionCard = ({
<Link
onClick={onEnter}
to={`/session/${id}`}
className={`relative h-52 bg-white rounded-custom-m px-5 py-6 transition-all duration-200 ease-in-out hover:-translate-y-1.5 shadow-8
className={`relative h-52 bg-white rounded-custom-m px-5 py-6 transition-all duration-200 ease-in-out hover:-translate-y-1.5 border-custom-s border-gray-200
${inProgress === false ? "hover:shadow-16 hover:ring-1 hover:ring-green-200" : ""}`}
>
<div className="flex flex-col justify-between">
Expand Down
11 changes: 5 additions & 6 deletions frontend/src/pages/SessionListPage/view/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ const Tab = ({ currentTab, tabStatus, setCurrentTab }: TabProps) => {
return (
<>
<button
className={`w-32 h-full
${
currentTab === tabStatus
? "text-green-600 text-semibold-r"
: "text-semibold-r text-gray-400"
}`}
className={`w-28 h-full
${currentTab === tabStatus
? "text-green-600 text-semibold-r"
: "text-semibold-r text-gray-400"
}`}
onClick={() => setCurrentTab(tabStatus)}
>
<span
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/SessionListPage/view/TabContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface CategoryTabProps {

const TabContainer = ({ currentTab, setCurrentTab }: CategoryTabProps) => {
return (
<section className="relative border-b flex gap-4 ">
<section className="relative border-b flex gap-4">
<Tab
currentTab={currentTab}
tabStatus={false}
Expand All @@ -19,7 +19,7 @@ const TabContainer = ({ currentTab, setCurrentTab }: CategoryTabProps) => {
setCurrentTab={setCurrentTab}
/>
<div
className={`absolute -z-0 pointer-events-none top-0 ${currentTab ? "left-36" : "left-0"} transition-all duration-200 pt-4 py-2 p-1 border-b border-green-200 h-full w-32`}
className={`absolute -z-0 pointer-events-none top-0 ${currentTab ? "left-32" : "left-0"} transition-all duration-200 pt-4 py-2 p-1 border-b border-green-200 h-full w-28`}
></div>
</section>
);
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/pages/SessionPage/hooks/useBlockNavigate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ const useBlockNavigate = () => {
}
}, [blocker]);

useEffect(() => {
return () => {
if (blocker) setShouldBlock(false);
};
}, []);

useEffect(() => {
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
e.preventDefault();
Expand Down
Loading