diff --git a/1-4/css/auth.css b/1-4/css/auth.css
deleted file mode 100644
index d67fcce6..00000000
--- a/1-4/css/auth.css
+++ /dev/null
@@ -1,146 +0,0 @@
-.material-symbols-outlined {
- font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
-}
-* {
- color: var(--gray-800);
-}
-html,
-body {
- height: 100%;
-}
-
-.auth_container {
- min-height: 100%;
- min-width: 16rem;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.width_container {
- max-width: 40rem;
- width: 100%;
-}
-
-.user_input label,
-.user_input input,
-.auth.btn {
- display: block;
- width: 100%;
-}
-
-.user_input label {
- font-size: 1.125rem;
- font-weight: 700;
- margin-bottom: 1rem;
-}
-
-.user_input input,
-.auth.btn {
- margin-bottom: 1.5rem;
-}
-
-.logo {
- justify-content: center;
- margin-bottom: 2.5rem;
-}
-.logo h1 {
- font-size: 4.15rem;
-}
-.logo_img {
- max-width: 6.5rem;
-}
-
-.user_input input {
- background-color: var(--gray-100);
- border-radius: 0.75rem;
- padding: 0 1.5rem;
-}
-
-.error_text {
- position: absolute;
- bottom: -1rem;
- left: 0;
- font-size: 0.875rem;
- color: #ff0000;
-}
-
-.auth.btn {
- font-size: 1.25rem;
- font-weight: 600;
- background-color: var(--gray-400);
-}
-
-.input_wrap {
- position: relative;
-}
-.password_icon {
- position: absolute;
- right: 1.5rem;
- top: 50%;
- transform: translateY(-50%);
- cursor: pointer;
-}
-
-.icon_cover {
- width: 2.625rem;
- height: 2.625rem;
- border-radius: 50%;
- overflow: hidden;
-}
-.sns_icon {
- display: block;
-}
-
-.auth_text a {
- color: var(--primary-100);
- border-bottom: 0.06rem solid var(--primary-100);
-}
-
-.simple_login {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 1.5rem;
- background-color: #e6f2ff;
- padding: 1rem 1.5rem;
- border-radius: var(--border-8);
-}
-
-.simple_login span {
- word-break: keep-all;
- font-weight: 500;
-}
-.sns_login {
- display: flex;
- justify-content: flex-end;
- gap: 1rem;
-}
-
-.auth_helper {
- text-align: center;
-}
-.auth_link {
- margin-left: 0.3rem;
-}
-
-@media screen and (max-width: 767px) {
- .auth_container {
- padding: 1rem;
- }
-
- .logo h1 {
- font-size: 2rem;
- }
- .logo_img {
- max-width: 3.25rem;
- }
- .user_input label {
- font-size: 0.875rem;
- margin-bottom: 0.5rem;
- }
-
- .width_container {
- max-width: 25rem;
- }
-}
diff --git a/1-4/css/common.css b/1-4/css/common.css
deleted file mode 100644
index 19cdf7df..00000000
--- a/1-4/css/common.css
+++ /dev/null
@@ -1,141 +0,0 @@
-* {
- color: var(--gray-700);
-}
-h2,
-.main span {
- font-weight: 700;
-}
-h2 {
- font-size: 2.5rem;
- word-break: keep-all;
- line-height: 3rem;
-}
-
-section:not(.main) {
- background-color: #cfe5ff;
-}
-
-.intro,
-.ending {
- padding-top: 4.2rem;
-}
-.flex_div {
- height: 33rem;
- display: flex;
- justify-content: space-between;
- align-items: flex-end;
-}
-.title_div {
- margin-bottom: 6.25rem;
- width: 30rem;
-}
-.ending .title_div {
- margin-bottom: 11rem;
-}
-
-.intro h2 {
- margin-bottom: 2rem;
-}
-
-.intro .shopping {
- display: block;
- font-size: 1.25rem;
- text-align: center;
- width: 21rem;
-}
-
-.main img {
- max-width: 36.75rem;
-}
-.main .card {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 3.5rem;
- padding: 7rem 0;
-}
-.main .card.right {
- flex-direction: row-reverse;
- text-align: right;
-}
-
-.card_info span {
- display: block;
- font-size: 1.125rem;
- color: var(--primary-100);
- margin-bottom: 1.4rem;
-}
-.card_info h2 {
- width: 18rem;
- margin-bottom: 2.8rem;
-}
-.card_info p {
- font-size: 1.5rem;
-}
-
-@media screen and (max-width: 1199px) {
- .flex_div {
- flex-direction: column;
- justify-content: flex-end;
- align-items: center;
- gap: 5rem;
- text-align: center;
- }
- .intro .flex_div {
- height: 48rem;
- }
- .ending .flex_div {
- height: 58rem;
- }
- .title_div {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- width: 100%;
- }
-
- .card_outter {
- margin: 1.5rem;
- }
-
- .main .card.right {
- flex-direction: column;
- }
- .main .card {
- flex-direction: column;
- align-items: unset;
- padding: 0 0 3.25rem;
- }
- .main img {
- max-width: 100%;
- border-radius: var(--border-16);
- }
- .card_info h2 {
- width: 100%;
- font-size: 2rem;
- margin-bottom: 1.5rem;
- }
- .card_info p {
- font-size: 1.125rem;
- }
-}
-
-@media screen and (max-width: 767px) {
- .intro,
- .ending {
- padding-top: 0;
- }
-
- .flex_div {
- height: 33.75rem;
- }
-
- .title_div {
- max-width: 21rem;
- }
-
- .main .card {
- padding: 1.5rem 0;
- }
-}
diff --git a/1-4/css/global.css b/1-4/css/global.css
deleted file mode 100644
index 50f84f97..00000000
--- a/1-4/css/global.css
+++ /dev/null
@@ -1,139 +0,0 @@
-* {
- box-sizing: border-box;
- margin: 0;
- font-family: "Pretendard", sans-serif;
-}
-:root {
- --primary-100: #3692ff;
- --primary-200: #1967d6;
- --primary-300: #1251aa;
- --gray-50: #f9fafb;
- --gray-100: #f3f4f6;
- --gray-200: #e5e7eb;
- --gray-400: #9ca3af;
- --gray-500: #6b7280;
- --gray-600: #4b5563;
- --gray-700: #374151;
- --gray-800: #1f2937;
- --gray-900: #111827;
- --error-red: #f74747;
-}
-
-.width_container {
- max-width: 70rem;
- margin: 0 auto;
-}
-
-a,
-.btn {
- color: var(--gray-100);
-}
-.text_tall {
- line-height: 3.5rem;
-}
-.btn.text_tall {
- border-radius: 2.5rem;
-}
-.btn {
- background-color: var(--primary-100);
-}
-a.btn:hover {
- background-color: var(--primary-200);
-}
-img {
- width: 100%;
-}
-
-header {
- position: sticky;
- top: 0;
- background-color: #fff;
- box-shadow: 0 0.3rem 1.875rem rgba(0, 0, 0, 0.1);
- z-index: 9999;
-}
-header > div {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 1.4rem 0;
-}
-
-header .logo_img {
- max-width: 2.5rem;
- max-height: 2.5rem;
- width: 100%;
-}
-a.logo {
- display: flex;
- align-items: center;
- gap: 1rem;
-}
-h1 {
- color: var(--primary-100);
- white-space: nowrap;
-}
-header h1 {
- font-size: 2rem;
-}
-
-header a.login {
- display: block;
- max-width: 8rem;
- font-size: 1rem;
- text-align: center;
- flex-basis: 8rem;
- padding: 0.7rem 2.1rem;
- border-radius: var(--border-8);
-}
-
-footer {
- background-color: var(--gray-900);
-}
-.footer_container {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 1.4rem 0 3.5rem;
- font-size: 1rem;
-}
-
-footer .faq,
-footer .sns_wrapper {
- display: flex;
- gap: 0.625rem;
-}
-
-@media screen and (max-width: 1920px) {
- header {
- padding: 0 12.5rem;
- }
-}
-
-@media screen and (max-width: 1199px) {
- header {
- padding: 0 1.5rem;
- }
- .footer_container {
- margin: 0 6.5rem;
- }
- footer .company_since {
- color: var(--gray-100);
- }
-}
-
-@media screen and (max-width: 767px) {
- header {
- padding: 0 1rem;
- }
-
- .footer_container {
- flex-wrap: wrap;
- margin: 0 auto;
- padding: 2rem;
- }
- .company_since {
- order: 3;
- width: 100%;
- padding-top: 3.75rem;
- }
-}
diff --git a/1-4/css/import.css b/1-4/css/import.css
deleted file mode 100644
index ef4277dc..00000000
--- a/1-4/css/import.css
+++ /dev/null
@@ -1,4 +0,0 @@
-@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined");
-
-@import url("reset.css");
-@import url("global.css");
diff --git a/1-4/css/reset.css b/1-4/css/reset.css
deleted file mode 100644
index 50f66215..00000000
--- a/1-4/css/reset.css
+++ /dev/null
@@ -1,170 +0,0 @@
-html,
-body,
-div,
-span,
-object,
-iframe,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-p,
-blockquote,
-pre,
-abbr,
-address,
-cite,
-code,
-del,
-dfn,
-em,
-img,
-ins,
-kbd,
-q,
-samp,
-small,
-strong,
-sub,
-sup,
-var,
-b,
-i,
-dl,
-dt,
-dd,
-ol,
-ul,
-li,
-fieldset,
-form,
-label,
-legend,
-table,
-caption,
-tbody,
-tfoot,
-thead,
-tr,
-th,
-td,
-article,
-aside,
-canvas,
-details,
-figcaption,
-figure,
-footer,
-header,
-hgroup,
-menu,
-nav,
-section,
-summary,
-time,
-mark,
-audio,
-video {
- margin: 0;
- padding: 0;
- border: 0;
- outline: 0;
- vertical-align: baseline;
- background: transparent;
- font-style: normal;
-}
-ul {
- line-height: normal;
-}
-body {
- line-height: 1;
-}
-
-article,
-aside,
-details,
-figcaption,
-figure,
-footer,
-header,
-hgroup,
-menu,
-nav,
-section {
- display: block;
-}
-
-ul,
-ol {
- list-style: none;
-}
-
-blockquote,
-q {
- quotes: none;
-}
-
-blockquote:before,
-blockquote:after,
-q:before,
-q:after {
- content: "";
- content: none;
-}
-
-a {
- margin: 0;
- padding: 0;
- font-size: 100%;
- text-decoration: none;
- vertical-align: baseline;
- background: transparent;
-}
-
-/* change colours to suit your needs */
-ins {
- background-color: #ff9;
- color: #000;
- text-decoration: none;
-}
-
-/* change colours to suit your needs */
-mark {
- background-color: transparent;
- color: #000;
- font-weight: bold;
-}
-
-del {
- text-decoration: line-through;
-}
-
-abbr[title],
-dfn[title] {
- border-bottom: 1px dotted;
- cursor: help;
-}
-
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-/* change border colour to suit your needs */
-hr {
- display: block;
- height: 1px;
- border: 0;
- border-top: 1px solid #cccccc;
- margin: 1em 0;
- padding: 0;
-}
-input, button{
- border: none;
-}
-input,
-select {
- vertical-align: middle;
-}
diff --git a/1-4/faq.html b/1-4/faq.html
deleted file mode 100644
index 2a0ed93c..00000000
--- a/1-4/faq.html
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 판다마켓
-
-
-
-
-
-
-
diff --git a/1-4/image/logo.png b/1-4/image/logo.png
deleted file mode 100644
index b8626892..00000000
Binary files a/1-4/image/logo.png and /dev/null differ
diff --git a/1-4/image/meta_padamarket.png b/1-4/image/meta_padamarket.png
deleted file mode 100644
index d9def111..00000000
Binary files a/1-4/image/meta_padamarket.png and /dev/null differ
diff --git a/1-4/image/sns01.png b/1-4/image/sns01.png
deleted file mode 100644
index 39fed4aa..00000000
Binary files a/1-4/image/sns01.png and /dev/null differ
diff --git a/1-4/image/sns02.png b/1-4/image/sns02.png
deleted file mode 100644
index 45c46baa..00000000
Binary files a/1-4/image/sns02.png and /dev/null differ
diff --git a/1-4/image/sns03.png b/1-4/image/sns03.png
deleted file mode 100644
index 0b4c07a8..00000000
Binary files a/1-4/image/sns03.png and /dev/null differ
diff --git a/1-4/image/sns04.png b/1-4/image/sns04.png
deleted file mode 100644
index 272c3841..00000000
Binary files a/1-4/image/sns04.png and /dev/null differ
diff --git a/1-4/image/sns_google.png b/1-4/image/sns_google.png
deleted file mode 100644
index 97d45425..00000000
Binary files a/1-4/image/sns_google.png and /dev/null differ
diff --git a/1-4/image/sns_kakao.png b/1-4/image/sns_kakao.png
deleted file mode 100644
index 54c40bbf..00000000
Binary files a/1-4/image/sns_kakao.png and /dev/null differ
diff --git a/1-4/index.html b/1-4/index.html
deleted file mode 100644
index 756ab4ad..00000000
--- a/1-4/index.html
+++ /dev/null
@@ -1,129 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 판다마켓
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
-
-
- -
-
-
-
Hot item
-
인기 상품을 확인해 보세요
-
가장 HOT한 중고거래 물품을
판다 마켓에서 확인해 보세요
-
-
- -
-
-
-
Search
-
구매를 원하는 상품을 검색하세요
-
구매하고 싶은 물품은 검색해서
쉽게 찾아보세요
-
-
- -
-
-
-
Register
-
판매를 원하는 상품을 등록하세요
-
어떤 물건이든 판매하고 싶은 상품을
쉽게 등록하세요
-
-
-
-
-
-
-
-
-
-
믿을 수 있는
판다마켓 중고 거래
-
-

-
-
-
-
-
-
diff --git a/1-4/items.html b/1-4/items.html
deleted file mode 100644
index a125abf2..00000000
--- a/1-4/items.html
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 판다마켓
-
-
-
-
-
-
-
diff --git a/1-4/js/auth.js b/1-4/js/auth.js
deleted file mode 100644
index 22ece1d5..00000000
--- a/1-4/js/auth.js
+++ /dev/null
@@ -1,165 +0,0 @@
-const inputs = document.querySelectorAll('.input_group');
-const authBtn = document.querySelector('.auth.btn');
-
-//input상태 저장소
-const inputValidState = {};
-
-//forEach 공통 변수
-function parseGroupInput(group){
- const input = group.querySelector('.input_text');
- const errorText = group.querySelector('.error_text');
- const type = group.dataset.type;
-
- return { input, errorText, type };
-}
-
-//error_text 초기화
-function clearError(target, text){
- target.style.border = '';
- text.textContent = '';
-}
-
-//label변수
-function getLabelText(input) {
- const label = input.closest('.input_group')?.querySelector('label');
- const labelText = label.textContent.trim();
-
- return labelText;
-
-}
-
-//에러메세지
-const ERROR_MESSAGES = {
- REQUIRED: (field) => `${field}을(를) 입력해주세요.`,
- EMAIL: '잘못된 이메일 형식입니다.',
- PASSWORD_LENGTH: '비밀번호를 8자 이상 입력해주세요.',
- PASSWORD_MISMATCH: '비밀번호가 일치하지 않습니다.'
-};
-
-//password 일치 비교
-function validatePasswordMatch(){
- const pw = document.querySelector('#user_password');
- const pwCheck = document.querySelector('#user_password_check');
- const pwCheckError = pwCheck?.closest('.input_wrap')?.querySelector('.error_text');
-
- if(pw && pwCheck){
- if(pw?.value !== pwCheck?.value){
- pwCheckError.textContent = ERROR_MESSAGES.PASSWORD_MISMATCH;
- inputValidState[pw] = false;
- inputValidState[pwCheck] = false;
- }else{
- clearError(pwCheck, pwCheckError);
- inputValidState[pw] = true;
- inputValidState[pwCheck] = true;
- }
- }
-}
-
-//에러메세지 적용
-function validateInput(type, value){
- const emailPattern = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,}$/;
-
- switch(type){
- case 'email':
- return emailPattern.test(value) ? '' : ERROR_MESSAGES.EMAIL;
- case 'password':
- return value.length < 8 ? ERROR_MESSAGES.PASSWORD_LENGTH : '';
- default:
- return '';
- }
-}
-
-//오류메세지 이벤트
-function attachValidationHandlers(){
- inputs.forEach(group =>{
- const {input, errorText, type} = parseGroupInput(group);
-
- inputValidState[type] = false;
-
- //focusout
- input.addEventListener('focusout', (e) => {
- const target = e.target;
- const values = target.value.trim();
-
- const labelText = getLabelText(target);
-
- //빈 값
- if(values === ''){
- target.style.border = '1px solid #ff0000';
- errorText.textContent = ERROR_MESSAGES.REQUIRED(labelText);
- inputValidState[type] = false;
- return;
- }
-
- //input 오류메세지
- const errorMessage = validateInput(type, values);
-
- if (errorMessage) {
- errorText.textContent = errorMessage;
- input.style.border = '1px solid #ff0000';
- inputValidState[type] = false;
- } else {
- clearError(input, errorText);
- inputValidState[type] = true;
- }
-
- //비밀번호 일치 비교
- if (type === 'password' || type === 'password_check') {
- validatePasswordMatch();
- }
- updateAuthButtonState();
- });
- });
-}
-
-//모든 input 작성 확인
-//true, false값 반환
-function updateInputState(){
- return Object.values(inputValidState).every(Boolean);
-}
-
-//submit버튼 활성화
-function updateAuthButtonState(){
-
- const isvalid = updateInputState();
-
- authBtn.disabled = !isvalid;
- authBtn.style.backgroundColor = isvalid ? 'var(--primary-100)' : 'var(--gray-400)';
- authBtn.style.cursor = isvalid ? 'pointer' : 'not-allowed';
-}
-
-//실시간 반영
-inputs.forEach(group => {
- const {input} = parseGroupInput(group);
-
- ['input', 'change', 'focusout'].forEach(eventName => {
- input.addEventListener(eventName, () => {
- updateAuthButtonState();
- });
-});
-});
-
-
-
-//패스워드 아이콘 활성화/비활성화
-const passwordIcon = document.querySelectorAll('.password_icon');
-
-passwordIcon.forEach( icons => {
- const prevInput = icons.previousElementSibling;
-
- icons.addEventListener('click', (e) => {
- const target = e.target;
-
- if(prevInput.type === 'password'){
- target.classList.add('show');
- prevInput.type = 'text'
- target.textContent = 'visibility'
- }else{
- target.classList.remove('show');
- prevInput.type = 'password'
- target.textContent = 'visibility_off'
- }
- });
-});
-
-attachValidationHandlers();
\ No newline at end of file
diff --git a/1-4/login.html b/1-4/login.html
deleted file mode 100644
index 277e81ed..00000000
--- a/1-4/login.html
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 판다마켓
-
-
-
-
-
-
-
-
-
diff --git a/1-4/privacy.html b/1-4/privacy.html
deleted file mode 100644
index 0b22264a..00000000
--- a/1-4/privacy.html
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 판다마켓
-
-
-
-
-
-
-
diff --git a/1-4/signup.html b/1-4/signup.html
deleted file mode 100644
index adce7490..00000000
--- a/1-4/signup.html
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 판다마켓
-
-
-
-
-
-
-
-
-
diff --git a/global.d.ts b/global.d.ts
index 66b24566..c3b758f3 100644
--- a/global.d.ts
+++ b/global.d.ts
@@ -1,4 +1,2 @@
-declare module "*.png" {
- const value: string;
- export default value;
-}
+//경로 설정
+declare module "*.png";
diff --git a/jsconfig.json b/jsconfig.json
index 20aab394..2872410b 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -5,6 +5,7 @@
"paths": {
"@/*": ["src/*"]
},
+ "types": ["vite-plugin-svgr/client"],
"jsx": "react-jsx",
"allowSyntheticDefaultImports": true, //default 없이 import 허용
"module": "esnext",
@@ -16,6 +17,6 @@
"checkJs": true // 타입검사
},
//컴파일 검사
- "include": ["src", "global.d.ts"],
+ "include": ["src", "global.d.ts", "vite-env.d.ts"],
"exclude": ["node_modules", "dist", "build", "coverage"]
}
diff --git a/package-lock.json b/package-lock.json
index 99530653..3d5f20f5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,8 +18,8 @@
},
"devDependencies": {
"@eslint/js": "^9.29.0",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
+ "@types/react": "^19.1.10",
+ "@types/react-dom": "^19.1.7",
"@vitejs/plugin-react-swc": "^3.10.2",
"eslint": "^9.29.0",
"eslint-plugin-react-hooks": "^5.2.0",
@@ -1800,18 +1800,18 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "19.1.8",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
- "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
+ "version": "19.1.10",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz",
+ "integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==",
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
- "version": "19.1.6",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
- "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
+ "version": "19.1.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz",
+ "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
"dev": true,
"license": "MIT",
"peerDependencies": {
diff --git a/package.json b/package.json
index a3eb036c..09ca5206 100644
--- a/package.json
+++ b/package.json
@@ -20,8 +20,8 @@
},
"devDependencies": {
"@eslint/js": "^9.29.0",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
+ "@types/react": "^19.1.10",
+ "@types/react-dom": "^19.1.7",
"@vitejs/plugin-react-swc": "^3.10.2",
"eslint": "^9.29.0",
"eslint-plugin-react-hooks": "^5.2.0",
diff --git a/src/App.jsx b/src/App.jsx
index b6589767..9f717904 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,18 +1,23 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";
+import "@/styles/variables.css";
import HomePage from "@/pages/home";
import ItemsPage from "@/pages/items";
import LoginPage from "@/pages/LoginPage";
import SignupPage from "@/pages/SignupPage";
-import AddItem from "@/pages/AddItem";
+import AddItem from "@/pages/addItem";
import HomeLayout from "@/layout/HomeLayout.jsx";
import DefaultLayout from "@/layout/DefaultLayout.jsx";
-import NoLayout from "@/layout/NoLayout.jsx";
import { ResetStyle } from "@/styles/ResetStyle";
import { GlobalStyle } from "@/styles/GlobalStyle";
import { ToastContainer } from "react-toastify";
+localStorage.setItem(
+ "accessToken",
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTksInNjb3BlIjoiYWNjZXNzIiwiaWF0IjoxNzU1Nzc0MjIwLCJleHAiOjE3NTU3NzYwMjAsImlzcyI6InNwLXBhbmRhLW1hcmtldCJ9.4OaXBQYM8vVusQW7bw8-H8JnBpP1rApa3z-tXiQ0VAY"
+);
+
function App() {
return (
@@ -29,10 +34,8 @@ function App() {
} />
- }>
- } />
- } />
-
+ } />
+ } />
}>
} />
diff --git a/src/apis/apiRequest.js b/src/apis/apiRequest.js
index 12e98c61..5275bbc2 100644
--- a/src/apis/apiRequest.js
+++ b/src/apis/apiRequest.js
@@ -4,17 +4,31 @@ const BASE_URL = "https://panda-market-api.vercel.app";
async function apiRequest(path, options = {}, isJson = true) {
try {
+ const token = localStorage.getItem("accessToken");
+
+ console.log(localStorage.getItem("accessToken"));
+ const headers = {
+ ...(options.headers || {}),
+ };
+
+ if (token) {
+ headers["Authorization"] = `Bearer ${token}`;
+ }
+
+ // FormData 이미지는 json이 아니다.
+ let body = options.body;
+ if (!(body instanceof FormData)) {
+ headers["Content-Type"] = "application/json";
+ }
+
const res = await fetch(`${BASE_URL}${path}`, {
...options,
- headers: {
- "Content-Type": "application/json",
- ...(options.headers || {}),
- },
+ headers,
});
if (!res.ok) {
- const errorText = await res.text();
- throw new Error(`[${res.status}] ${errorText}`);
+ const error_text = await res.text();
+ throw new Error(`[${res.status}] ${error_text}`);
}
return isJson ? await res.json() : true;
diff --git a/src/apis/products.js b/src/apis/products.js
index e03894f5..d5159097 100644
--- a/src/apis/products.js
+++ b/src/apis/products.js
@@ -14,6 +14,12 @@ export const getProducts = ({
);
};
+export const postProduct = (data) =>
+ apiRequest(`/products`, {
+ method: "POST",
+ body: JSON.stringify(data),
+ });
+
//특정 상품
export const getProductById = (id) => apiRequest(`/products/${id}`);
@@ -27,3 +33,12 @@ export const patchProduct = (id, data) =>
//상품 삭제
export const deleteProduct = (id) =>
apiRequest(`/products/${id}`, { method: "DELETE" }, false);
+
+//favorite 추가
+export const postFavorite = (id, data) =>
+ apiRequest(`/products/${id}/favorite`, { method: "POST" });
+
+//facorite 삭제
+
+export const deleteFavorite = (id) =>
+ apiRequest(`/products/${id}/favorite`, { method: "DELETE" }, false);
diff --git a/src/assets/Img_home_01.png b/src/assets/Img_home_01.png
deleted file mode 100644
index e54eafe5..00000000
Binary files a/src/assets/Img_home_01.png and /dev/null differ
diff --git a/src/assets/Img_home_02.png b/src/assets/Img_home_02.png
deleted file mode 100644
index 4afa3b92..00000000
Binary files a/src/assets/Img_home_02.png and /dev/null differ
diff --git a/src/assets/Img_home_03.png b/src/assets/Img_home_03.png
deleted file mode 100644
index d795d28c..00000000
Binary files a/src/assets/Img_home_03.png and /dev/null differ
diff --git a/src/assets/Img_home_bottom.png b/src/assets/Img_home_bottom.png
deleted file mode 100644
index 78bfd8e5..00000000
Binary files a/src/assets/Img_home_bottom.png and /dev/null differ
diff --git a/src/assets/Img_home_top.png b/src/assets/Img_home_top.png
deleted file mode 100644
index a7976703..00000000
Binary files a/src/assets/Img_home_top.png and /dev/null differ
diff --git a/src/assets/NoImage.png b/src/assets/NoImage.png
deleted file mode 100644
index 365248e4..00000000
Binary files a/src/assets/NoImage.png and /dev/null differ
diff --git a/src/assets/arrow_icon.png b/src/assets/arrow_icon.png
deleted file mode 100644
index 0b0e9d56..00000000
Binary files a/src/assets/arrow_icon.png and /dev/null differ
diff --git a/src/assets/favorit_Icon.png b/src/assets/favorit_Icon.png
deleted file mode 100644
index 63b6e81c..00000000
Binary files a/src/assets/favorit_Icon.png and /dev/null differ
diff --git a/src/assets/favorit_fill_Icon.png b/src/assets/favorit_fill_Icon.png
deleted file mode 100644
index 1f23dbdd..00000000
Binary files a/src/assets/favorit_fill_Icon.png and /dev/null differ
diff --git a/1-4/image/Img_home_bottom.png b/src/assets/home_bottom.png
similarity index 100%
rename from 1-4/image/Img_home_bottom.png
rename to src/assets/home_bottom.png
diff --git a/1-4/image/Img_home_01.png b/src/assets/home_main01.png
similarity index 100%
rename from 1-4/image/Img_home_01.png
rename to src/assets/home_main01.png
diff --git a/1-4/image/Img_home_02.png b/src/assets/home_main02.png
similarity index 100%
rename from 1-4/image/Img_home_02.png
rename to src/assets/home_main02.png
diff --git a/1-4/image/Img_home_03.png b/src/assets/home_main03.png
similarity index 100%
rename from 1-4/image/Img_home_03.png
rename to src/assets/home_main03.png
diff --git a/1-4/image/Img_home_top.png b/src/assets/home_top.png
similarity index 100%
rename from 1-4/image/Img_home_top.png
rename to src/assets/home_top.png
diff --git a/src/assets/ic_arrow_down.svg b/src/assets/ic_arrow_down.svg
new file mode 100644
index 00000000..358e4b87
--- /dev/null
+++ b/src/assets/ic_arrow_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_arrow_left_active.svg b/src/assets/ic_arrow_left_active.svg
new file mode 100644
index 00000000..4b110c20
--- /dev/null
+++ b/src/assets/ic_arrow_left_active.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_arrow_left_back.svg b/src/assets/ic_arrow_left_back.svg
new file mode 100644
index 00000000..00d9784e
--- /dev/null
+++ b/src/assets/ic_arrow_left_back.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_arrow_left_inactive.svg b/src/assets/ic_arrow_left_inactive.svg
new file mode 100644
index 00000000..1daeca5c
--- /dev/null
+++ b/src/assets/ic_arrow_left_inactive.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_arrow_left_white.svg b/src/assets/ic_arrow_left_white.svg
new file mode 100644
index 00000000..3ec2fc77
--- /dev/null
+++ b/src/assets/ic_arrow_left_white.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_arrow_right_active.svg b/src/assets/ic_arrow_right_active.svg
new file mode 100644
index 00000000..0ad718ef
--- /dev/null
+++ b/src/assets/ic_arrow_right_active.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_arrow_right_inactive.svg b/src/assets/ic_arrow_right_inactive.svg
new file mode 100644
index 00000000..764302b6
--- /dev/null
+++ b/src/assets/ic_arrow_right_inactive.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_arrow_right_white.svg b/src/assets/ic_arrow_right_white.svg
new file mode 100644
index 00000000..c3d3f7d4
--- /dev/null
+++ b/src/assets/ic_arrow_right_white.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_best_badge.svg b/src/assets/ic_best_badge.svg
new file mode 100644
index 00000000..8470f48f
--- /dev/null
+++ b/src/assets/ic_best_badge.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/ic_check.svg b/src/assets/ic_check.svg
new file mode 100644
index 00000000..baa4aa3b
--- /dev/null
+++ b/src/assets/ic_check.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/assets/ic_close.svg b/src/assets/ic_close.svg
new file mode 100644
index 00000000..f6674f7f
--- /dev/null
+++ b/src/assets/ic_close.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/assets/ic_favorit_Icon.svg b/src/assets/ic_favorit_Icon.svg
new file mode 100644
index 00000000..f620d41f
--- /dev/null
+++ b/src/assets/ic_favorit_Icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_favorit_fill_Icon.svg b/src/assets/ic_favorit_fill_Icon.svg
new file mode 100644
index 00000000..b47cb5ca
--- /dev/null
+++ b/src/assets/ic_favorit_fill_Icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_plus.svg b/src/assets/ic_plus.svg
new file mode 100644
index 00000000..5bb9abf5
--- /dev/null
+++ b/src/assets/ic_plus.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/assets/ic_search.svg b/src/assets/ic_search.svg
new file mode 100644
index 00000000..d323ead6
--- /dev/null
+++ b/src/assets/ic_search.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_search_gray.svg b/src/assets/ic_search_gray.svg
new file mode 100644
index 00000000..d9cc31ea
--- /dev/null
+++ b/src/assets/ic_search_gray.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_sns01.svg b/src/assets/ic_sns01.svg
new file mode 100644
index 00000000..b9c9d493
--- /dev/null
+++ b/src/assets/ic_sns01.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_sns02.svg b/src/assets/ic_sns02.svg
new file mode 100644
index 00000000..14a6069a
--- /dev/null
+++ b/src/assets/ic_sns02.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_sns03.svg b/src/assets/ic_sns03.svg
new file mode 100644
index 00000000..699b5380
--- /dev/null
+++ b/src/assets/ic_sns03.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/assets/ic_sns04.svg b/src/assets/ic_sns04.svg
new file mode 100644
index 00000000..0b9337b0
--- /dev/null
+++ b/src/assets/ic_sns04.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/ic_sort.svg b/src/assets/ic_sort.svg
new file mode 100644
index 00000000..657b44f9
--- /dev/null
+++ b/src/assets/ic_sort.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/ic_user.svg b/src/assets/ic_user.svg
new file mode 100644
index 00000000..0480454d
--- /dev/null
+++ b/src/assets/ic_user.svg
@@ -0,0 +1,24 @@
+
diff --git a/src/assets/ic_visibillity_off.svg b/src/assets/ic_visibillity_off.svg
new file mode 100644
index 00000000..57e303f5
Binary files /dev/null and b/src/assets/ic_visibillity_off.svg differ
diff --git a/src/assets/ic_visibillity_on.svg b/src/assets/ic_visibillity_on.svg
new file mode 100644
index 00000000..43a5af17
--- /dev/null
+++ b/src/assets/ic_visibillity_on.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/left_arrow.png b/src/assets/left_arrow.png
deleted file mode 100644
index 1252102c..00000000
Binary files a/src/assets/left_arrow.png and /dev/null differ
diff --git a/src/assets/noImage.png b/src/assets/noImage.png
new file mode 100644
index 00000000..5a106e57
Binary files /dev/null and b/src/assets/noImage.png differ
diff --git a/src/assets/right_arrow.png b/src/assets/right_arrow.png
deleted file mode 100644
index a03ad8af..00000000
Binary files a/src/assets/right_arrow.png and /dev/null differ
diff --git a/src/assets/sns01.png b/src/assets/sns01.png
deleted file mode 100644
index 39fed4aa..00000000
Binary files a/src/assets/sns01.png and /dev/null differ
diff --git a/src/assets/sns02.png b/src/assets/sns02.png
deleted file mode 100644
index 45c46baa..00000000
Binary files a/src/assets/sns02.png and /dev/null differ
diff --git a/src/assets/sns03.png b/src/assets/sns03.png
deleted file mode 100644
index 0b4c07a8..00000000
Binary files a/src/assets/sns03.png and /dev/null differ
diff --git a/src/assets/sns04.png b/src/assets/sns04.png
deleted file mode 100644
index 272c3841..00000000
Binary files a/src/assets/sns04.png and /dev/null differ
diff --git a/src/assets/user_icon.png b/src/assets/user_icon.png
deleted file mode 100644
index 827c5f2f..00000000
Binary files a/src/assets/user_icon.png and /dev/null differ
diff --git a/src/components/layout/Footer.style.js b/src/components/layout/Footer.style.js
index a9b8ea37..18ada85d 100644
--- a/src/components/layout/Footer.style.js
+++ b/src/components/layout/Footer.style.js
@@ -1,4 +1,4 @@
-import { media } from "@/styles/commomStyle";
+import { media } from "@/styles/commonStyle";
import styled from "styled-components";
export const FooterStyle = styled.footer`
diff --git a/src/components/layout/Header.jsx b/src/components/layout/Header.jsx
index b4dac2e2..c4d3ac1f 100644
--- a/src/components/layout/Header.jsx
+++ b/src/components/layout/Header.jsx
@@ -1,13 +1,14 @@
import { Link, NavLink } from "react-router-dom";
import pandaLogo from "@/assets/logo.png";
-import userIcon from "@/assets/user_icon.png";
+import UserIcon from "@/assets/ic_user.svg";
import { HeaderStyle } from "@/components/layout/Header.style";
+import { WidthContainer } from "@/styles/commonStyle";
function Header() {
return (
-
+

@@ -34,8 +35,8 @@ function Header() {
-
-
+
+
);
}
diff --git a/src/components/layout/Header.style.js b/src/components/layout/Header.style.js
index a8f930d9..82c6a113 100644
--- a/src/components/layout/Header.style.js
+++ b/src/components/layout/Header.style.js
@@ -1,4 +1,4 @@
-import { flexCenter, fullSize, media, textStyles } from "@/styles/commomStyle";
+import { flexCenter, fullSize, media, textStyles } from "@/styles/commonStyle";
import { pxToRem } from "@/utils/pxToRem";
import styled from "styled-components";
diff --git a/src/components/layout/HomeFooter.jsx b/src/components/layout/HomeFooter.jsx
index d1da3699..e1c8e61c 100644
--- a/src/components/layout/HomeFooter.jsx
+++ b/src/components/layout/HomeFooter.jsx
@@ -1,27 +1,33 @@
import { Link } from "react-router-dom";
import { snsImages } from "@/components/layout/snsData";
import { FooterStyle } from "@/components/layout/Footer.style";
+import { WidthContainer } from "@/styles/commonStyle";
function HomeFooter() {
return (
-
-
©codeit - 2024
-
- Privacy Policy
- FAQ
-
+
+
+
©codeit - 2024
+
+ Privacy Policy
+ FAQ
+
-
- {snsImages.map((sns, index) => (
- -
-
-
-
-
- ))}
-
-
+
+ {snsImages.map((sns, index) => {
+ const SnsIcon = sns.svg;
+ return (
+ -
+
+
+
+
+ );
+ })}
+
+
+
);
}
diff --git a/src/components/layout/HomeHeader.jsx b/src/components/layout/HomeHeader.jsx
index 85b628d2..7675a2e7 100644
--- a/src/components/layout/HomeHeader.jsx
+++ b/src/components/layout/HomeHeader.jsx
@@ -1,11 +1,12 @@
import { Link } from "react-router-dom";
import pandaLogo from "@/assets/logo.png";
import { HeaderStyle } from "@/components/layout/Header.style";
+import { WidthContainer } from "@/styles/commonStyle";
function HomeHeader() {
return (
-
+
판다마켓
@@ -13,7 +14,7 @@ function HomeHeader() {
로그인
-
+
);
}
diff --git a/src/components/layout/snsData.js b/src/components/layout/snsData.js
index eeba1577..8a51ac9e 100644
--- a/src/components/layout/snsData.js
+++ b/src/components/layout/snsData.js
@@ -1,27 +1,27 @@
-import imgSNS01 from "@/assets/sns01.png";
-import imgSNS02 from "@/assets/sns02.png";
-import imgSNS03 from "@/assets/sns03.png";
-import imgSNS04 from "@/assets/sns04.png";
+import imgSNS01 from "@/assets/ic_sns01.svg";
+import imgSNS02 from "@/assets/ic_sns02.svg";
+import imgSNS03 from "@/assets/ic_sns03.svg";
+import imgSNS04 from "@/assets/ic_sns04.svg";
export const snsImages = [
{
href: "https://www.facebook.com/",
- img: imgSNS01,
+ svg: imgSNS01,
alt: "facebook",
},
{
href: "https://x.com/",
- img: imgSNS02,
+ svg: imgSNS02,
alt: "X",
},
{
href: "https://www.youtube.com/",
- img: imgSNS03,
+ svg: imgSNS03,
alt: "youtube",
},
{
href: "https://www.instagram.com/",
- img: imgSNS04,
+ svg: imgSNS04,
alt: "instagram",
},
];
diff --git a/src/layout/NoLayout.jsx b/src/layout/NoLayout.jsx
deleted file mode 100644
index fd51323b..00000000
--- a/src/layout/NoLayout.jsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Outlet } from "react-router-dom";
-
-function NoLayout() {
- return ;
-}
-
-export default NoLayout;
diff --git a/src/pages/AddItem.jsx b/src/pages/AddItem.jsx
deleted file mode 100644
index 4d635afd..00000000
--- a/src/pages/AddItem.jsx
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function AddItem() {
- return (
- <>
-
- >
- );
-}
diff --git a/src/pages/addItem/AddImage.jsx b/src/pages/addItem/AddImage.jsx
new file mode 100644
index 00000000..8062c105
--- /dev/null
+++ b/src/pages/addItem/AddImage.jsx
@@ -0,0 +1,114 @@
+import AddIcon from "@/assets/ic_plus.svg";
+import XIcon from "@/assets/ic_close.svg";
+import { useRef, useState } from "react";
+import apiRequest from "@/apis/apiRequest";
+import styled from "styled-components";
+import { pxToRem } from "@/utils/pxToRem";
+import { flexCenter, fullSize, textStyles } from "@/styles/commonStyle";
+
+export default function AddImage({ imgTitle = "상품이미지", onChange }) {
+ const [imageUrl, setImageUrl] = useState(null);
+ const fileInputRef = useRef(null);
+
+ const handleFileChange = async (e) => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ const formData = new FormData();
+ formData.append("image", file);
+
+ try {
+ const data = await apiRequest("/images/upload", {
+ method: "POST",
+ body: formData,
+ headers: {},
+ });
+ setImageUrl(data.url);
+ onChange?.(data.url);
+ } catch (error) {
+ console.error("이미지 등록 실패:", error);
+ }
+ };
+
+ const handleClick = () => {
+ fileInputRef.current.click();
+ };
+
+ const handleRemove = () => {
+ setImageUrl(null);
+ fileInputRef.current.value = "";
+ onChange?.(null);
+ };
+
+ return (
+
+
+
+
+ {imageUrl ? (
+
+

+
+
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+}
+
+export const AddImageStyle = styled.div`
+ #item_img {
+ position: relative;
+ width: ${pxToRem(282)};
+ height: ${pxToRem(282)};
+ cursor: pointer;
+ margin: 1rem 0 2rem;
+ }
+
+ .image_add {
+ ${fullSize}
+ }
+ .image_add.btn {
+ position: relative;
+ }
+
+ .icon_position {
+ height: 100%;
+ ${flexCenter}
+ flex-direction: column;
+ }
+
+ .close.bnt {
+ position: absolute;
+ background-color: transparent;
+ width: ${pxToRem(20)};
+ height: ${pxToRem(20)};
+ right: ${pxToRem(13)};
+ top: ${pxToRem(14)};
+ }
+`;
diff --git a/src/pages/addItem/InputField.jsx b/src/pages/addItem/InputField.jsx
new file mode 100644
index 00000000..50f5aa82
--- /dev/null
+++ b/src/pages/addItem/InputField.jsx
@@ -0,0 +1,37 @@
+import { useState } from "react";
+
+export default function InputField({
+ id,
+ label,
+ type = "text",
+ placeholder,
+ errorMessage,
+ ...inputProps
+}) {
+ const [touched, setTouched] = useState(false);
+
+ const handleBlur = (e) => {
+ setTouched(true);
+ if (inputProps.onBlur) inputProps.onBlur(e);
+ };
+
+ const value = inputProps.value || "";
+
+ let errors =
+ touched && (!value.trim() || (type === "number" && !/^\d+$/.test(value)));
+
+ return (
+
+
+
+ {errors && {errorMessage}}
+
+ );
+}
diff --git a/src/pages/addItem/InputTag.jsx b/src/pages/addItem/InputTag.jsx
new file mode 100644
index 00000000..364a5787
--- /dev/null
+++ b/src/pages/addItem/InputTag.jsx
@@ -0,0 +1,94 @@
+import XIcon from "@/assets/ic_close.svg";
+import { flexCenter } from "@/styles/commonStyle";
+import { pxToRem } from "@/utils/pxToRem";
+import { useState } from "react";
+import styled from "styled-components";
+
+export default function InputTag({ onChange }) {
+ const [tags, setTags] = useState([]);
+ const [inputValue, setInputValue] = useState("");
+ const [includeValue, setIncludeValue] = useState(false);
+
+ const handleKeyDown = (e) => {
+ if (e.key === "Enter" && inputValue.trim()) {
+ e.preventDefault();
+
+ if (tags.some((tag) => tag.label === inputValue.trim())) {
+ setIncludeValue(true);
+ return;
+ }
+
+ const new_tag = {
+ id: crypto.randomUUID(),
+ label: inputValue.trim(),
+ };
+
+ const newTags = [...tags, new_tag];
+ setTags(newTags);
+ onChange?.(newTags.map((tag) => tag.label));
+ setInputValue("");
+ }
+ };
+
+ const removeTag = (id) => {
+ const newTags = tags.filter((tag) => tag.id !== id);
+ setTags(newTags);
+ onChange?.(newTags.map((tag) => tag.label));
+ };
+
+ return (
+
+
+
+ setInputValue(e.target.value)}
+ onKeyDown={handleKeyDown}
+ />
+ {includeValue && (
+ 이미 입력된 태그입니다
+ )}
+
+
+ {tags.map((tag) => (
+ -
+ #{tag.label}
+
+
+ ))}
+
+
+
+ );
+}
+
+const TagStyle = styled.div`
+ .input_style {
+ margin-bottom: ${pxToRem(14)};
+ }
+
+ .tags {
+ display: flex;
+ align-items: center;
+ flex-wrap: nowrap;
+ gap: ${pxToRem(12)};
+ }
+
+ .tag {
+ ${flexCenter}
+ line-height: ${pxToRem(36)};
+ background-color: var(--gray-100);
+ padding: 0 1rem;
+ border-radius: ${pxToRem(26)};
+ }
+
+ .tag_icon {
+ background-color: transparent;
+ }
+`;
diff --git a/src/pages/addItem/index.jsx b/src/pages/addItem/index.jsx
new file mode 100644
index 00000000..cc9cf5f1
--- /dev/null
+++ b/src/pages/addItem/index.jsx
@@ -0,0 +1,109 @@
+import { postProduct } from "@/apis/products";
+import AddImage from "@/pages/addItem/AddImage";
+import { AddItemStyle } from "@/pages/addItem/index.style";
+import InputField from "@/pages/addItem/InputField";
+import InputTag from "@/pages/addItem/InputTag";
+import { WidthContainer } from "@/styles/commonStyle";
+import { useState } from "react";
+import { useNavigate } from "react-router-dom";
+
+export default function AddItem() {
+ const navigate = useNavigate();
+
+ const [form, setForm] = useState({
+ itemName: "",
+ itemIntroduction: "",
+ itemPrice: "",
+ images: [],
+ tags: [],
+ });
+
+ const errorMessage = {
+ itemName: "상품명을 입력해주세요",
+ itemIntroduction: "상품 소개를 입력해주세요",
+ itemPrice: "숫자로 된 판매 가격을 입력해주세요",
+ };
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setForm((prev) => ({ ...prev, [name]: value }));
+ };
+
+ const isFormValid =
+ form.itemName.trim() &&
+ form.itemIntroduction.trim() &&
+ form.itemPrice.trim();
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ try {
+ const data = {
+ name: form.itemName,
+ description: form.itemIntroduction,
+ price: Number(form.itemPrice),
+ images: form.images,
+ tags: form.tags,
+ };
+ await postProduct(data);
+
+ navigate("/items");
+ } catch (error) {
+ console.error("상품 등록 실패:", error);
+ }
+ };
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/pages/addItem/index.style.js b/src/pages/addItem/index.style.js
new file mode 100644
index 00000000..05de0ab8
--- /dev/null
+++ b/src/pages/addItem/index.style.js
@@ -0,0 +1,69 @@
+import { pxToRem } from "@/utils/pxToRem";
+import styled from "styled-components";
+import "@/styles/variables.css";
+import { textStyles } from "@/styles/commonStyle";
+
+export const AddItemStyle = styled.div`
+ .input_list {
+ margin-bottom: ${pxToRem(60)};
+ }
+
+ .item_add {
+ display: flex;
+ justify-content: space-between;
+ margin: ${pxToRem(50)} 0 ${pxToRem(24)};
+ }
+ .item_submit {
+ width: ${pxToRem(74)};
+ height: ${pxToRem(42)};
+ border-radius: var(--border-8);
+ }
+
+ .item_submit:disabled {
+ background-color: var(--gray-400);
+ cursor: unset;
+ }
+
+ label {
+ ${textStyles["text-2lg-bold"]}
+ }
+
+ .image_add,
+ .input_style {
+ background-color: var(--gray-100);
+ border-radius: ${pxToRem(12)};
+ ${textStyles["text-lg-regular"]}
+ }
+
+ .image_add,
+ .input_style::placeholder {
+ color: var(--gray-400);
+ }
+
+ .input_style {
+ display: block;
+ width: 100%;
+ outline: none;
+ line-height: ${pxToRem(56)};
+ padding: 0 ${pxToRem(24)};
+ margin: 1rem 0 2rem;
+ color: var(--gray-800);
+ }
+
+ .has_error {
+ position: relative;
+ }
+
+ .has_error .input_style {
+ outline: 2px solid var(--error-red);
+ box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.08);
+ }
+
+ .errorText {
+ position: absolute;
+ display: block;
+ bottom: ${pxToRem(-30)};
+ color: var(--error-red);
+ ${textStyles["text-lg-regular"]}
+ }
+`;
diff --git a/src/pages/home/HomeEnding.jsx b/src/pages/home/HomeEnding.jsx
index e5955a40..ffb8eb15 100644
--- a/src/pages/home/HomeEnding.jsx
+++ b/src/pages/home/HomeEnding.jsx
@@ -1,18 +1,21 @@
-import EndingImage from "@/assets/Img_home_bottom.png";
+import EndingImage from "@/assets/home_bottom.png";
+import { WidthContainer } from "@/styles/commonStyle";
function HomeEnding() {
return (
-
-
-
- 믿을 수 있는
-
- 판다마켓 중고 거래
-
+
+
+
+
+ 믿을 수 있는
+
+ 판다마켓 중고 거래
+
+
+
-
-
+
);
}
diff --git a/src/pages/home/HomeIntro.jsx b/src/pages/home/HomeIntro.jsx
index 5193f649..62def1ee 100644
--- a/src/pages/home/HomeIntro.jsx
+++ b/src/pages/home/HomeIntro.jsx
@@ -1,19 +1,22 @@
import { Link } from "react-router-dom";
-import IntroImage from "@/assets/img_home_top.png";
+import IntroImage from "@/assets/home_top.png";
+import { WidthContainer } from "@/styles/commonStyle";
function HomeIntro() {
return (
-
-
-
일상의 모든 물건을 거래해 보세요
-
- 구매하러 가기
-
+
+
+
+
일상의 모든 물건을 거래해 보세요
+
+ 구매하러 가기
+
+
+
-
-
+
);
}
diff --git a/src/pages/home/HomeMain.jsx b/src/pages/home/HomeMain.jsx
index fa72ece1..ae3bfb78 100644
--- a/src/pages/home/HomeMain.jsx
+++ b/src/pages/home/HomeMain.jsx
@@ -1,17 +1,18 @@
import { homeCards } from "@/pages/home/homeCardData";
import HomeCard from "@/pages/home/HomeCard";
+import { WidthContainer } from "@/styles/commonStyle";
function HomeMain() {
return (
-
+
{homeCards.map((card) => (
))}
-
+
);
}
diff --git a/src/pages/home/homeCardData.js b/src/pages/home/homeCardData.js
index e6aad9a4..07b34bab 100644
--- a/src/pages/home/homeCardData.js
+++ b/src/pages/home/homeCardData.js
@@ -1,6 +1,6 @@
-import Homecard1 from "@/assets/Img_home_01.png";
-import Homecard2 from "@/assets/Img_home_02.png";
-import Homecard3 from "@/assets/Img_home_03.png";
+import Homecard1 from "@/assets/home_main01.png";
+import Homecard2 from "@/assets/home_main02.png";
+import Homecard3 from "@/assets/home_main03.png";
export const homeCards = [
{
diff --git a/src/pages/items/ItemList.jsx b/src/pages/items/ItemList.jsx
index e1ee581a..c48e0bbd 100644
--- a/src/pages/items/ItemList.jsx
+++ b/src/pages/items/ItemList.jsx
@@ -1,30 +1,43 @@
-import { ItemListStyle } from "@/pages/items/ItemList.style";
-import noImage from "@/assets/NoImage.png";
-import favoriteIcon from "@/assets/favorit_Icon.png";
-import favoriteFillIcon from "@/assets/favorit_fill_Icon.png";
-import { useState } from "react";
-import { patchProduct } from "@/apis/products";
+import { useEffect, useState } from "react";
+import { deleteFavorite, getProductById, postFavorite } from "@/apis/products";
import { Link } from "react-router-dom";
+import { ItemListStyle } from "@/pages/items/ItemList.style";
+import noImage from "@/assets/noImage.png";
+import FavoriteIcon from "@/assets/ic_favorit_Icon.svg";
+import FavoriteFillIcon from "@/assets/ic_favorit_fill_Icon.svg";
+
export default function ItemList({ id, images, name, price, favoriteCount }) {
const [isClick, setIsClick] = useState(false);
const [count, setCount] = useState(favoriteCount);
const [loading, setLoading] = useState(false);
+ useEffect(() => {
+ async function fetchDetail() {
+ const product = await getProductById(id);
+ if (product) {
+ setIsClick(product.isFavorite);
+ setCount(product.favoriteCount);
+ }
+ }
+ fetchDetail();
+ }, [id]);
+
const handleFavoriteClick = async () => {
if (loading) return;
setLoading(true);
- setIsClick((prev) => !prev);
- const newCount = count + (!isClick ? +1 : -1);
+ const nextState = !isClick;
+ setIsClick(nextState);
+ const newCount = count + (nextState ? +1 : -1);
setCount(newCount);
try {
- await patchProduct(id, { favoriteCount: newCount });
+ nextState ? await postFavorite(id) : await deleteFavorite(id);
} catch (error) {
console.error("좋아요 변경 실패:", error);
- setIsClick(!isClick);
+ setIsClick(isClick);
setCount(count);
} finally {
setLoading(false);
@@ -45,16 +58,16 @@ export default function ItemList({ id, images, name, price, favoriteCount }) {
/>
-
+
{name}
-
+
{Number(price).toLocaleString()} 원
-
+ {isClick ? (
+
+ ) : (
+
+ )}
{count}
diff --git a/src/pages/items/ItemList.style.js b/src/pages/items/ItemList.style.js
index 7077076d..dadba7e6 100644
--- a/src/pages/items/ItemList.style.js
+++ b/src/pages/items/ItemList.style.js
@@ -1,4 +1,4 @@
-import { textStyles } from "@/styles/commomStyle";
+import { textStyles } from "@/styles/commonStyle";
import { pxToRem } from "@/utils/pxToRem";
import styled from "styled-components";
diff --git a/src/pages/items/ItemsOrder.jsx b/src/pages/items/ItemsOrder.jsx
index 8b321a0c..ade0db69 100644
--- a/src/pages/items/ItemsOrder.jsx
+++ b/src/pages/items/ItemsOrder.jsx
@@ -1,11 +1,11 @@
import styled from "styled-components";
-
+import ArrowIcon from "@/assets/ic_arrow_down.svg";
+import SortIcon from "@/assets/ic_sort.svg";
import { pxToRem } from "@/utils/pxToRem";
-import arrowIcon from "@/assets/arrow_icon.png";
-import { flexCenter, textStyles } from "@/styles/commomStyle";
+import { flexCenter, media, textStyles } from "@/styles/commonStyle";
import { useState } from "react";
-export default function ItemsOrder({ orderBy, setOrderBy }) {
+export default function ItemsOrder({ orderBy, setOrderBy, device }) {
const [isOpen, setIsOpen] = useState(false);
const handleSelect = (value) => {
@@ -17,8 +17,14 @@ export default function ItemsOrder({ orderBy, setOrderBy }) {
setIsOpen((prev) => !prev)}>
-
{orderBy === "recent" ? "최신순" : "인기순"}
-

+ {device === "mobile" ? (
+
+ ) : (
+ <>
+
{orderBy === "recent" ? "최신순" : "인기순"}
+
+ >
+ )}
{isOpen && (
@@ -57,6 +63,10 @@ const Div = styled.div`
.listLabel {
line-height: ${pxToRem(42)};
gap: ${pxToRem(28)};
+ ${media.mobile} {
+ width: ${pxToRem(42)};
+ height: ${pxToRem(42)};
+ }
}
.arrow_icon {
@@ -73,6 +83,9 @@ const Div = styled.div`
display: block;
text-align: center;
background-color: #fff;
+ ${media.mobile} {
+ right: 0;
+ }
}
.listOption:hover {
diff --git a/src/pages/items/ItemsSearch.jsx b/src/pages/items/ItemsSearch.jsx
index 92555b12..1c04a724 100644
--- a/src/pages/items/ItemsSearch.jsx
+++ b/src/pages/items/ItemsSearch.jsx
@@ -1,9 +1,11 @@
-import { flexCenter, textStyles } from "@/styles/commomStyle";
+import { flexCenter, fullSize, textStyles } from "@/styles/commonStyle";
import { pxToRem } from "@/utils/pxToRem";
import { useState } from "react";
// import { toast } from "react-toastify";
import styled from "styled-components";
+import SearchIcon from "@/assets/ic_search_gray.svg";
+
export default function ItemsSearch({ setSearchInput }) {
const [inputValue, setInputValue] = useState("");
@@ -20,6 +22,7 @@ export default function ItemsSearch({ setSearchInput }) {
};
const handleClick = () => {
+ // 빈칸 입력시 경고 토스트
// if (inputValue.trim() === "") {
// toast.warning("검색어를 입력해주세요", {
// toastId: "empty-search",
@@ -43,13 +46,27 @@ export default function ItemsSearch({ setSearchInput }) {
return (
);
}
@@ -60,17 +77,37 @@ const Div = styled.div`
height: ${pxToRem(42)};
padding: 0 ${pxToRem(13)};
}
-
- .search_input{
+
+ .search_wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+ padding: 0;
+ }
+
+ .search_input {
+ ${fullSize}
+ padding-left: ${pxToRem(40)};
background-color: var(--gray-100);
border-radius: var(--border-10) 0 0 var(--border-10);
}
- .search_btn{
- ${textStyles["text-lg-medium"]}
+ .search_input:focus {
+ outline: none;
+ }
+
+ .search_icon {
+ position: absolute;
+ left: 1rem;
+ top: 50%;
+ transform: translateY(-50%);
+ pointer-events: none;
+ }
+
+ .search_btn {
+ ${textStyles["text-lg-medium"]}
background-color: var(--primary-100);
color: #fff;
border-radius: 0 var(--border-10) var(--border-10) 0;
}
-
`;
diff --git a/src/pages/items/PageList.jsx b/src/pages/items/PageList.jsx
index 8b6ac793..66e3cc7d 100644
--- a/src/pages/items/PageList.jsx
+++ b/src/pages/items/PageList.jsx
@@ -1,6 +1,6 @@
-import LArrow from "@/assets/left_arrow.png";
-import RArrow from "@/assets/right_arrow.png";
-import { flexCenter } from "@/styles/commomStyle";
+import LArrow from "@/assets/ic_arrow_left_active.svg";
+import RArrow from "@/assets/ic_arrow_right_active.svg";
+import { flexCenter } from "@/styles/commonStyle";
import { pxToRem } from "@/utils/pxToRem";
import styled from "styled-components";
@@ -34,7 +34,7 @@ export default function PageList({ totalCount, page, setPage, getAllList }) {
onClick={goPrevGroup}
disabled={!prevGroup}
>
-

+
{Array.from({ length: groupEnd - groupStart + 1 }, (_, i) => {
@@ -56,7 +56,7 @@ export default function PageList({ totalCount, page, setPage, getAllList }) {
onClick={goNextGroup}
disabled={!nextGroup}
>
-

+
@@ -71,9 +71,14 @@ const PageListStyle = styled.div`
}
.page_btn {
+ ${flexCenter}
border: 1px solid var(--gray-200);
}
+ .page_arrow_img > path {
+ transform: translate(${pxToRem(0.6)}, ${pxToRem(0.6)});
+ }
+
.page_list,
.page_btn {
outline: none;
@@ -91,9 +96,4 @@ const PageListStyle = styled.div`
.page_list:hover .page_btn.arrow {
background-color: transparent;
}
-
- .page_arrow_img {
- width: ${pxToRem(7)};
- height: ${pxToRem(12)};
- }
`;
diff --git a/src/pages/items/index.jsx b/src/pages/items/index.jsx
index 272a1313..ea032ffc 100644
--- a/src/pages/items/index.jsx
+++ b/src/pages/items/index.jsx
@@ -7,14 +7,15 @@ import { handleFetch } from "@/utils/handleFetch";
import ItemList from "@/pages/items/ItemList";
import ItemsSearch from "@/pages/items/ItemsSearch";
import ItemsOrder from "@/pages/items/ItemsOrder";
-import { ItemsStyle } from "@/pages/items/Items.style";
+import { ItemsStyle } from "@/pages/items/index.style";
import PageList from "@/pages/items/PageList";
+import { breakpoints, media } from "@/styles/commonStyle";
//반응형
const getDeviceType = () => {
const width = window.innerWidth;
- if (width >= 1200) return "pc";
- if (width >= 768) return "tablet";
+ if (width >= breakpoints.pc) return "pc";
+ if (width >= breakpoints.tablet) return "tablet";
return "mobile";
};
@@ -82,12 +83,6 @@ function ItemsPage() {
"best"
);
- //맨 처음 렌더링했을 때
- useEffect(() => {
- fetchProducts();
- fetchBestProducts();
- }, []);
-
// 페이지이동, 검색, 정렬변경, 화면 크기 변경했을 때 렌더링
useEffect(() => {
fetchProducts();
@@ -105,7 +100,7 @@ function ItemsPage() {
>
) : (
<>
-
베스트 상품
+
베스트 상품
{bestProducts.map((item) => (
-
@@ -116,13 +111,17 @@ function ItemsPage() {
-
전체 상품
+
전체 상품
-
-
-
-
+
+
diff --git a/src/pages/items/Items.style.js b/src/pages/items/index.style.js
similarity index 73%
rename from src/pages/items/Items.style.js
rename to src/pages/items/index.style.js
index bebbeb5c..b1003f7b 100644
--- a/src/pages/items/Items.style.js
+++ b/src/pages/items/index.style.js
@@ -1,4 +1,4 @@
-import { flexCenter, fullSize, media, textStyles } from "@/styles/commomStyle";
+import { flexCenter, media, textStyles } from "@/styles/commonStyle";
import { pxToRem } from "@/utils/pxToRem";
import styled from "styled-components";
@@ -7,8 +7,10 @@ export const ItemsStyle = styled.div`
margin: 0 auto;
padding: ${pxToRem(24)} 0 ${pxToRem(58)};
- h3 {
+ .itmes_title {
${textStyles["text-xl-bold"]}
+ line-height: ${pxToRem(42)};
+ margin-bottom: 0.5rem;
}
/* 상품 nav */
@@ -18,11 +20,21 @@ export const ItemsStyle = styled.div`
align-items: center;
margin-bottom: ${pxToRem(24)};
line-height: ${pxToRem(42)};
+
+ ${media.mobile} {
+ position: relative;
+ flex-wrap: wrap;
+ }
}
.item_order {
${flexCenter};
gap: ${pxToRem(12)};
+
+ ${media.mobile} {
+ width: 100%;
+ justify-content: space-between;
+ }
}
.item_all_list {
@@ -36,6 +48,12 @@ export const ItemsStyle = styled.div`
border-radius: var(--border-10);
background-color: var(--primary-100);
padding: 0 ${pxToRem(23)};
+
+ ${media.mobile} {
+ position: absolute;
+ right: 0;
+ top: 0;
+ }
}
/* 상품 이미지 */
@@ -44,6 +62,16 @@ export const ItemsStyle = styled.div`
gap: ${pxToRem(24)};
}
+ .product_img {
+ aspect-ratio: 1 / 1;
+ object-fit: cover;
+ width: 100%;
+ }
+
+ .best_items > li {
+ flex: 1;
+ }
+
.all_items {
display: grid;
grid-template-columns: repeat(5, 1fr);
@@ -58,25 +86,6 @@ export const ItemsStyle = styled.div`
}
}
- .best_item .product_img {
- width: ${pxToRem(282)};
- height: ${pxToRem(282)};
-
- ${media.tablet} {
- width: ${pxToRem(343)};
- height: ${pxToRem(343)};
- }
- }
-
- .all_items .product_img {
- width: ${pxToRem(221)};
- height: ${pxToRem(221)};
- ${media.mobile} {
- width: ${pxToRem(168)};
- height: ${pxToRem(168)};
- }
- }
-
${media.tablet} {
max-width: ${pxToRem(696)};
}
diff --git a/src/styles/GlobalStyle.js b/src/styles/GlobalStyle.js
index 2d70cf8c..5e956316 100644
--- a/src/styles/GlobalStyle.js
+++ b/src/styles/GlobalStyle.js
@@ -1,3 +1,4 @@
+import { media } from "@/styles/commonStyle";
import { pxToRem } from "@/utils/pxToRem";
import { createGlobalStyle } from "styled-components";
@@ -8,35 +9,6 @@ export const GlobalStyle = createGlobalStyle`
font-family: "Pretendard", sans-serif;
}
- :root {
- /* 색상 */
- --primary-100: #3692ff;
- --primary-200: #1967d6;
- --primary-300: #1251aa;
- --gray-50: #f9fafb;
- --gray-100: #f3f4f6;
- --gray-200: #e5e7eb;
- --gray-400: #9ca3af;
- --gray-500: #6b7280;
- --gray-600: #4b5563;
- --gray-700: #374151;
- --gray-800: #1f2937;
- --gray-900: #111827;
- --error-red: #f74747;
-
- /* border-radius */
- --border-8: ${pxToRem(8)};
- --border-10: ${pxToRem(10)};
- --border-16: ${pxToRem(16)};
-
- }
-
- /* pc기준 */
- .width_container {
- max-width: 70rem;
- margin: 0 auto;
- }
-
a,
.btn {
color: var(--gray-100);
diff --git a/src/styles/commomStyle.js b/src/styles/commonStyle.js
similarity index 89%
rename from src/styles/commomStyle.js
rename to src/styles/commonStyle.js
index a6ff56a6..5ab5c111 100644
--- a/src/styles/commomStyle.js
+++ b/src/styles/commonStyle.js
@@ -1,10 +1,16 @@
-import { css } from "styled-components";
+import { pxToRem } from "@/utils/pxToRem";
+import styled, { css } from "styled-components";
// pc, tablet, mobie
+export const breakpoints = {
+ pc: 1200,
+ tablet: 768,
+ mobile: 375,
+};
+
export const media = {
- pc: "@media (min-width: 1200px)",
- tablet: "@media (min-width: 768px) and (max-width: 1199px)",
- mobile: "@media (min-width: 375px) and (max-width: 767px)",
+ tablet: `@media (max-width: ${breakpoints.pc - 1}px)`,
+ mobile: `@media (max-width: ${breakpoints.tablet - 1}px)`,
};
//공통 스타일
@@ -18,6 +24,17 @@ export const fullSize = css`
height: 100%;
`;
+export const WidthContainer = styled.div`
+ max-width: 70rem;
+ margin: 0 auto;
+ ${media.tablet} {
+ max-width: ${pxToRem(696)};
+ }
+ ${media.mobile} {
+ max-width: ${pxToRem(344)};
+ }
+`;
+
//폰트 스타일
export const textStyles = {
"text-3xl-bold": css`
diff --git a/src/styles/variables.css b/src/styles/variables.css
new file mode 100644
index 00000000..478c6c93
--- /dev/null
+++ b/src/styles/variables.css
@@ -0,0 +1,21 @@
+:root {
+ /* 색상 */
+ --primary-100: #3692ff;
+ --primary-200: #1967d6;
+ --primary-300: #1251aa;
+ --gray-50: #f9fafb;
+ --gray-100: #f3f4f6;
+ --gray-200: #e5e7eb;
+ --gray-400: #9ca3af;
+ --gray-500: #6b7280;
+ --gray-600: #4b5563;
+ --gray-700: #374151;
+ --gray-800: #1f2937;
+ --gray-900: #111827;
+ --error-red: #f74747;
+
+ /* border-radius */
+ --border-8: 0.5rem;
+ --border-10: 0.625rem;
+ --border-16: 1rem;
+}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 00000000..e7baf44c
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1,9 @@
+///
+
+declare module "*.svg" {
+ import * as React from "react";
+ const Component: React.FunctionComponent<
+ React.SVGProps & { title?: string }
+ >;
+ export default Component;
+}
diff --git a/vite.config.js b/vite.config.js
index 77c4c1c2..c64e5843 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,9 +1,16 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
+import svgr from "vite-plugin-svgr";
export default defineConfig({
- plugins: [react()],
+ plugins: [
+ react(),
+ svgr({
+ exportAsDefault: true, // 기본 export를 React 컴포넌트로
+ include: "**/*.svg", // 모든 .svg 파일 적용
+ }),
+ ],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),