diff --git a/package-lock.json b/package-lock.json
index 8a5ccf7..48a4df9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"axios": "^1.7.9",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-responsive": "^10.0.0",
"react-router": "^7.1.5",
"styled-components": "^6.1.15",
"zustand": "^5.0.3"
@@ -1953,6 +1954,12 @@
"node": ">=4"
}
},
+ "node_modules/css-mediaquery": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz",
+ "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==",
+ "license": "BSD"
+ },
"node_modules/css-to-react-native": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
@@ -2479,6 +2486,12 @@
"node": ">=8"
}
},
+ "node_modules/hyphenate-style-name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
+ "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2691,6 +2704,15 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/matchmediaquery": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.4.2.tgz",
+ "integrity": "sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==",
+ "license": "MIT",
+ "dependencies": {
+ "css-mediaquery": "^0.1.2"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -2788,6 +2810,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -2935,6 +2966,17 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -2997,6 +3039,12 @@
"react": "^18.3.1"
}
},
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -3007,6 +3055,24 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-responsive": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-10.0.0.tgz",
+ "integrity": "sha512-N6/UiRLGQyGUqrarhBZmrSmHi2FXSD++N5VbSKsBBvWfG0ZV7asvUBluSv5lSzdMyEVjzZ6Y8DL4OHABiztDOg==",
+ "license": "MIT",
+ "dependencies": {
+ "hyphenate-style-name": "^1.0.0",
+ "matchmediaquery": "^0.4.2",
+ "prop-types": "^15.6.1",
+ "shallow-equal": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/react-router": {
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.5.tgz",
@@ -3140,6 +3206,12 @@
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT"
},
+ "node_modules/shallow-equal": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz",
+ "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==",
+ "license": "MIT"
+ },
"node_modules/shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
diff --git a/package.json b/package.json
index 6ee0811..640eb7e 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"axios": "^1.7.9",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-responsive": "^10.0.0",
"react-router": "^7.1.5",
"styled-components": "^6.1.15",
"zustand": "^5.0.3"
diff --git a/src/hooks/.gitkeep b/src/hooks/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/src/hooks/useMediaQueries.ts b/src/hooks/useMediaQueries.ts
new file mode 100644
index 0000000..646036b
--- /dev/null
+++ b/src/hooks/useMediaQueries.ts
@@ -0,0 +1,20 @@
+import { useMediaQuery } from "react-responsive";
+
+const useMediaQueries = () => {
+ /*
+ * Union 작업중 콘텐츠 박스에서 767보다 더 작은 크기 조절이 필요해서 isTiny를 추가했습니다.
+ * 반응형 작업하실 때 한번더 봐주시고 고쳐주시면 좋을거 같습니다.
+ */
+ const isTiny = useMediaQuery({ query: "(max-width: 385px)" })
+ const isMobile = useMediaQuery({ query: "(max-width: 767px)" });
+ const isTablet = useMediaQuery({
+ query: "(min-width: 768px) and (max-width: 1023px)",
+ });
+ const isDesktop = useMediaQuery({
+ query: "(min-width: 1024px)"
+ });
+
+ return { isTiny, isMobile, isTablet, isDesktop };
+};
+
+export default useMediaQueries;
\ No newline at end of file
diff --git a/src/pages/Main/Union/ContentBox.styled.ts b/src/pages/Main/Union/ContentBox.styled.ts
new file mode 100644
index 0000000..421377d
--- /dev/null
+++ b/src/pages/Main/Union/ContentBox.styled.ts
@@ -0,0 +1,173 @@
+import styled from "styled-components";
+
+export const Container = styled.div<{ $isMobile: boolean }>`
+ width: 100%;
+ height: ${(props) => (props.$isMobile ? "auto" : "500px")};
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ position: relative;
+ padding: ${(props) => (props.$isMobile ? "20px 0" : "0")};
+`;
+
+// 테두리에 Gradient가 안돼서 박스 두개를 겹쳐서 테두리 Gradient를 만들었습니다.
+export const ContentBoxBorder = styled.div<{ $isMobile: boolean }>`
+ position: ${(props) => (props.$isMobile ? "relative" : "absolute")};
+ top: 0;
+ width: ${(props) => (props.$isMobile ? "90%" : "700px")};
+ height: ${(props) => (props.$isMobile ? "auto" : "500px")};
+
+ background: linear-gradient(
+ 90deg,
+ var(--FarmSystem_Orange) 0%,
+ var(--FarmSystem_Green02) 100%
+ );
+ padding: 5px;
+ border-radius: 20px;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 10;
+ margin: 0 auto;
+`;
+
+export const Content = styled.div`
+ width: 100%;
+ height: 100%;
+
+ background-color: var(--FarmSystem_White);
+ border-radius: 15px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+`;
+
+export const ContentInfoTextBox = styled.div<{ $isMobile: boolean }>`
+ width: ${(props) => (props.$isMobile ? "90%" : "600px")};
+ height: ${(props) => (props.$isMobile ? "auto" : "210px")};
+
+ color: var(--FarmSystem_Black);
+ font-style: normal;
+ font-weight: 500;
+ font-size: ${(props) => (props.$isMobile ? "20px" : "24px")};
+ line-height: ${(props) => (props.$isMobile ? "30px" : "35px")};
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+
+ margin-bottom: 35px;
+ padding-top: ${(props) => (props.$isMobile ? "20px" : "0")};
+ padding-bottom: ${(props) => (props.$isMobile ? "20px" : "0")};
+`;
+
+export const HighlightOrange = styled.span`
+ font-weight: 700;
+ color: var(--FarmSystem_Orange);
+`;
+
+export const ActivityTitle = styled.h3<{ $isMobile: boolean }>`
+ width: ${(props) => (props.$isMobile ? "auto" : "149px")};
+ height: ${(props) => (props.$isMobile ? "auto" : "38px")};
+
+ font-weight: 700;
+ font-size: ${(props) => (props.$isMobile ? "22px" : "24px")};
+ line-height: ${(props) => (props.$isMobile ? "30px" : "35px")};
+ color: var(--FarmSystem_Green01);
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+
+ margin-bottom: ${(props) => (props.$isMobile ? "7px" : "17px")};
+`;
+
+export const ActivityList = styled.ul<{ $isMobile: boolean }>`
+ width: ${(props) => (props.$isMobile ? "90%" : "577px")};
+ height: ${(props) => (props.$isMobile ? "auto" : "80px")};
+
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ align-items: flex-end;
+ align-content: flex-start;
+
+ gap: ${(props) => (props.$isMobile ? "1px 10px" : "10px 20px")};
+ list-style-type: disc;
+ justify-content: ${(props) => (props.$isMobile ? "center" : "initial")};
+`;
+
+export const Li = styled.li<{
+ $isMobile: boolean;
+ $isTiny?: boolean;
+}>`
+ height: 35px;
+
+ font-style: normal;
+ font-weight: 500;
+ font-size: ${(props) =>
+ props.$isMobile
+ ? props.$isTiny
+ ? "18px"
+ : "20px"
+ : "24px"};
+ line-height: ${(props) => (props.$isMobile ? "30px" : "35px")};
+ color: var(--FarmSystem_Black);
+
+ list-style-type: ${(props) =>
+ props.$isMobile && props.$isTiny ? "none" : "disc"};
+ list-style-position: ${(props) =>
+ props.$isMobile && props.$isTiny ? "none" : "outside"};
+ margin-left: ${(props) =>
+ props.$isMobile
+ ? props.$isTiny
+ ? "0px"
+ : "20px"
+ : "40px"};
+ text-align: center;
+`;
+
+export const GradientContainer = styled.div<{ $isMobile: boolean }>`
+ position: absolute;
+ top: 0;
+ height: 500px;
+ // 배경 Gradient를 모바일에서도 보이게 하려면 이 부분 고쳐주시면 됩니다!
+ display: ${(props) => (props.$isMobile ? "none" : "flex")};
+ gap: 80px;
+ justify-content: center;
+ align-items: center;
+`;
+
+export const GradientLeft = styled.div<{ $isMobile: boolean }>`
+ width: ${(props) => (props.$isMobile ? "300px" : "560px")};
+ height: ${(props) => (props.$isMobile ? "250px" : "400px")};
+
+ background: linear-gradient(
+ 270deg,
+ var(--FarmSystem_Orange) 50%,
+ var(--FarmSystem_White) 100%
+ );
+ opacity: 0.5;
+ border-radius: 20px;
+`;
+
+export const GradientRight = styled.div<{ $isMobile: boolean }>`
+ width: ${(props) => (props.$isMobile ? "300px" : "560px")};
+ height: ${(props) => (props.$isMobile ? "250px" : "400px")};
+
+ background: linear-gradient(
+ 90deg,
+ var(--FarmSystem_Green02) 50%,
+ var(--FarmSystem_White) 100%
+ );
+ opacity: 0.5;
+ border-radius: 20px;
+`;
diff --git a/src/pages/Main/Union/ContentBox.tsx b/src/pages/Main/Union/ContentBox.tsx
new file mode 100644
index 0000000..46f801a
--- /dev/null
+++ b/src/pages/Main/Union/ContentBox.tsx
@@ -0,0 +1,46 @@
+import * as S from "./ContentBox.styled";
+import useMediaQueries from "@/hooks/useMediaQueries";
+
+export default function ContentBox() {
+ const { isTiny, isMobile } = useMediaQueries();
+
+ return (
+
+ SW/AI 기초 역량을 다지기 위한 과정으로, 트랙 구분 없이 다섯 가지 트랙의 다양한 주제에 대한 SW 기본 역량을 다집니다. 각 트랙의 멘토가 제공하는 프로젝트와 스터디에 멘티로서 참여하게 됩니다.
신입생이라서
+어떤 트랙을 선택할지 고민되나요?
+