상세 컨텐츠

본문 제목

[React] 해커톤 웹 프로젝트 - 1

Web 개발

by javadocq 2024. 8. 8. 11:48

본문

728x90

이번에 무박 2일 해커톤을 진행하면서 React에 대해 많이 공부하고 실력이 많이 늘은 거 같아서 뿌듯하다... ㅎ

 

처음 하는 웹 프로젝트라서 프론트들과의 분담, 디자이너와의 갈등, 백엔드와의 데이터 연결 등 여러가지 많은 

 

어려움이 있었지만 그래도 팀원들이 열심히 해줘서 잘 끝낼 수 있었던 것 같다.

 

그래서 요번에는 우리팀이 해커톤 때 구상했던 웹 프로젝트를 블로그에 남기려고 한다.

 

서비스명 : Phorest (Photo + rest)

 

개발 인원 : 디자이너 1, 백엔드 2, 프론트 3

 

총 기간 : 3주 반 (아이디어 회의 3일 , 디자인 1주 , 개발 2주)

 

주제 : 사진을 보고 마음의 힐링을 할 수 있게 도와주는 서비스

 

개발 환경 : github , react, css , django

 

총 페이지는 10장으로 

 

Home, Gallery, Upload, Mypage, Signup, Search, Ranking, Goods, GoodsBuy, OneGallery 이렇게 구상을 하였다.

 

내가 개발한 페이지는 Mypage, Signup, Search, Ranking, Goods, GoodsBuy, OneGallery로 다 소개할 수는 없겠지만

 

그래도 한번 되는데까지 포스팅을 해보겠다.

 

<Ranking Page>

 

사진/일러스트들의 카테고리별 랭킹과 카테고리에 맞는 굿즈들을 표시해주는 페이지이다.

 

display 방식은 Grid로 2행 5열로 표시되게 해 놓았다. 

 

 

import React, { useState } from 'react';
import all_filter from '../assets/ranking-all-filter.svg';
import pet_filter from '../assets/ranking-pet-filter.svg';
import ocean_filter from '../assets/ranking-ocean-filter.svg';
import hill_filter from '../assets/ranking-hill-filter.svg';
import camping_filter from '../assets/ranking-camping-filter.svg';
import season_filter from '../assets/ranking-season-filter.svg';

import '../styles/Ranking.css';
import Foot from '../components/Foot.jsx';
import RankingImg from '../components/RankingImg.jsx';
import GoodsImg from '../components/GoodsImg.jsx';
import FilterButton from '../components/FilterButton.jsx';

const rankingFilterButtons = [
{ id: 'all-button', label: '전체', icon: all_filter },
{ id: 'pet-button', label: '반려동물', icon: pet_filter },
{ id: 'ocean-button', label: '바다', icon: ocean_filter },
{ id: 'hill-button', label: '산', icon: hill_filter },
{ id: 'camping-button', label: '캠핑', icon: camping_filter },
{ id: 'season-button', label: '계절', icon: season_filter }
];

const rankingIlustButtons = [
{ id: 'all-button', label: '전체', icon: all_filter },
{ id: 'character-button', label: '캐릭터', icon: season_filter },
{ id: 'animal-button', label: '동물', icon: season_filter },
{ id: 'sight-button', label: '사물', icon: season_filter },
{ id: 'etc-button', label: '기타', icon: season_filter }
]

export default function Ranking() {
const [selectedCategory, setSelectedCategory] = useState('all-button');

const handleCategoryChange = (categoryId) => {
setSelectedCategory(categoryId);
};
 
return (
<div className="ranking-wrap">
<div className="ranking-filter">
<FilterButton
filterButtons={rankingFilterButtons}
setCategory={handleCategoryChange}
/>
</div>

<div className="ranking-main-best">
<RankingImg count={6}/>
</div>
<div className="ranking-main-goods">
<h1 className='ranking-goods-title'>베스트 굿즈</h1>
<GoodsImg selectedCategory={selectedCategory}/>
</div>
<foot className="ranking-foot">
<Foot />
</foot>
</div>
);
}

 

Ranking Page에서는 카테고리별 버튼이 있는 FilterButton, 랭킹이미지가 있는 RankingImg, 카테고리에 맞는 굿즈들을 불러오는

 

RankingImg, 로고가 박혀있는 Foot 이렇게 4개의 컴포넌트들로 구성이 되어있다.

 

<RankingImg.jsx>

import React, { useEffect, useState } from 'react';
import rankingShadow from '../assets/ranking_shadow.png';
import axios from 'axios';
import '../styles/RankingImg.css';

const BASE_URL = "http://3.39.26.152:8000";

export default function RankingImg({ count }) {
const [ranking, setRanking] = useState([]);

const rankingGet = async () => {
try {
const response = await axios.get(`${BASE_URL}/api/galleries/ranking?type=사진&category=반려동물`);
// 응답이 배열인지 확인하고 설정
if (Array.isArray(response.data)) {
setRanking(response.data); // 배열로 설정
console.log(response.data);
} else {
console.error("응답이 배열이 아닙니다:", response.data);
}

} catch (error) {
console.error("Error fetching posts:", error);
}
};

useEffect(() => {
rankingGet();
}, []);

return (
<div className="rankingImg-wrap">
{ranking.slice(0, Math.min(count, ranking.length)).map((rankingItem, index) => (
<RankingImgBox index={index} ranking={rankingItem} />
))}
</div>
);
}

function RankingImgBox({ index, ranking }) {
return (
<div className="rankingImg-box">
<div className='best-ranking-number'>{index + 1}</div>
<div className='ranking-box-container'>
<img src={rankingShadow} alt="" className="best-ranking-shadow" />
<img src={`http://${ranking.image}`} alt="" className="best-ranking" />
<img src={`http://${ranking.profile_image}`} alt="" className="profile-img" />
<p className='best-ranking-p'>{ranking.title || '임시 글쓰기'}</p>
</div>
</div>
);
}

 

<GoodsImg.jsx>

 

import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import '../styles/GoodsImg.css';

const BASE_URL = "http://3.39.26.152:8000";

function GoodsImgBox({ image, name, price }) {
const navigate = useNavigate();

const handleClick = () => {
navigate('/goodsbuy', { state: { imgSrc: image, itemName: name, itemPrice: price } });
};

const formattedPrice = new Intl.NumberFormat().format(price);

return (
<div>
<button className="goodsImg-box" onClick={handleClick}>
<img src={`http://${image}`} alt="" className="best-goods-image" />
</button>
<div>
<p className='best-goods-description'>{name}</p>
<p className='best-goods-price'>{`${formattedPrice}원`}</p>
</div>
</div>
);
}

export default function GoodsImg({ selectedCategory, selectedSort }) {
const [goods, setGoods] = useState([]);

const fetchGoods = async () => {
try {
const sortValue = selectedSort || "최신순";
const category = selectedCategory === 'all-button' ? '전체' :
selectedCategory === 'pet-button' ? '반려동물' :
selectedCategory === 'ocean-button' ? '바다' :
selectedCategory === 'hill-button' ? '산' :
selectedCategory === 'camping-button' ? '캠핑' :
selectedCategory === 'season-button' ? '계절' :
selectedCategory === 'character-button' ? '캐릭터' :
selectedCategory === 'animal-button' ? '동물' :
selectedCategory === 'object-button' ? '사물' :
selectedCategory === 'etc-button' ? '기타' : '반려동물';
console.log(category);
console.log(sortValue);
const response = await axios.get(`${BASE_URL}/api/goods?category=${category}&page=1&sort=${sortValue}`);
if (Array.isArray(response.data)) {
setGoods(response.data);
console(response.data);
} else {
console.error("응답이 배열이 아닙니다:", response.data);
}
} catch (error) {
console.error("Error fetching goods data:", error);
}
};
 
useEffect(() => {
fetchGoods();
}, [selectedCategory, selectedSort]);

return (
<div className="goodsImg-wrap">
{goods.map((item) => (
<GoodsImgBox key={item.id} image={item.image} name={item.name} price={item.price} />
))}
</div>
);
}

 

 

<FilterButton.jsx>

 

import React, { useEffect, useState } from 'react';
import '../styles/FilterButton.css';

export default function FilterButton({ filterButtons, setCategory }) {
const [activeButton, setActiveButton] = useState('all-button');

const handleButtonClick = (buttonId) => {
setActiveButton(buttonId);
setCategory(buttonId);
};

useEffect(() => {
const buttons = document.querySelectorAll('.ranking-button');
buttons.forEach(button => {
if (button.id === activeButton) {
button.style.backgroundColor = '#2D2D2D';
button.style.color = 'white';
} else {
button.style.backgroundColor = '#FFF';
button.style.color = '#2D2D2D';
}
});
}, [activeButton]);

return (
<div className="filter-button-container">
{filterButtons.map(({ id, label, icon }) => (
<button
key={id}
id={id}
className={`ranking-button ${activeButton === id ? 'active' : ''}`}
onClick={() => handleButtonClick(id)}
>
{icon && <img src={icon} alt="" className="ranking-filter-img" />}
<div style={{ fontSize: '1rem', fontStyle: 'normal', fontWeight: '500' }}>{label}</div>
</button>
))}
</div>
);
}

 

 

<Foot.jsx>

 

import phorestLogo from '../assets/phorestLogo.png';

export default function Foot() {
return (<>
<img style={{height: "206.58px", width: "685px"}}src={phorestLogo} alt="로고 사진" className="ranking-foootlogo" />
</>)
}

 

'Web 개발' 카테고리의 다른 글

[React] 해커톤 웹 프로젝트 - 3  (0) 2024.08.10
[React] 해커톤 웹 프로젝트 - 2  (0) 2024.08.09
[React] onKeyDown  (0) 2024.08.07
[React] useEffect  (0) 2024.08.06
[React] useState  (0) 2024.08.05

관련글 더보기