https://github.com/thelight0804/subak
https://github.com/s-minii/subak
$$ \huge\textsf{\textbf{\color{#dc645b}수박마켓 (당근마켓 클론코딩)}} $$
<aside> <img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0fb2db70-d069-479e-8cd8-3f44fba669d3/blank.png" alt="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0fb2db70-d069-479e-8cd8-3f44fba669d3/blank.png" width="40px" /> 당근마켓 서비스를 클론코딩한 수박마켓 프로젝트입니다.
</aside>
<aside> 👉 日本語はこちらをご覧ください。
</aside>
https://github.com/thelight0804/subak/releases
Backend | Frontend |
---|---|
Java 11 | JavaScript |
Spring Boot 2.7.17 | React Native |
Spring Data JPA | Axios |
Swagger | Redux |
MariaDB | React Navigation |
Cloudinary | Async Storage |
StyleSheet |
React를 공부하다가 앱 개발에도 관심이 생겨 React Native를 사용했습니다. React Native는 JavaScript로 개발이 가능하며 Android 와 iOS 모바일 앱을 동시에 개발할 수 있습니다. 또한, React 개발자라면 누구든지 쉽게 앱을 개발할 수 있다는 장점이 있습니다.
예시 코드
<View style={shared.container}>
//...
<View style={styles.content}>
<Image
style={styles.image}
source={require('../../assets/image/launch_screen.png')}
/>
<View>
<Text style={[styles.text, styles.title]}>당신 근처의 수박</Text>
<Text style={styles.text}>동네라서 가능한 모든 것</Text>
<Text style={styles.text}>지금 내 동네를 선택하고 시작해보세요!</Text>
<TouchableOpacity>
//...
</View>
</View>
//...
</View>
React Native는 React와 달리 HTML 문법을 사용하지 않고, 대신 다양한 핵심 컴포넌트를 활용한다는 점을 배웠습니다. 예를 들어, <View>
는 레이아웃을 구성하는 컨테이너로서, HTML에서 <div>
와 유사한 기능을 수행합니다. <Text>
는 텍스트를 표현하고 스타일링하는 데 사용되며, 이는 HTML의 <p>
태그와 비슷한 역할을 합니다.
Axios는 HTTP 요청을 전송하는 데 쓰이는 라이브러리로, fetch와 달리 다양한 브라우저에서 호환되며 데이터를 자동으로 JSON 형식으로 변환해줍니다. 또한, 페이지를 새로고침하지 않고도 데이터를 주고받을 수 있습니다. 메인 페이지에서 사용자가 화면을 아래로 스크롤할 때 추가로 게시글 데이터를 받아오는 기능을 구현하기 위해서 Axios를 사용했습니다.
예시 코드
axios.get(`http://${Config.DB_IP}/posts?offset=${start*10}&limit=10`, {
headers: {
Authorization: `Bearer ${userToken}`,
},
timeout: 2000
})
.then(response => {
// 요청이 성공적으로 완료되었을 때
})
.catch(error => {
// 요청 중에 오류가 발생했을 때
});
Axios를 활용하여 서버로부터 게시물 데이터를 가져옵니다. headers 객체에서 사용자 인증을 거쳐 게시물 데이터를 안전하게 불러올 수 있습니다. 또한, 데이터를 성공적으로 가져온 경우와 오류가 발생한 경우를 구분하여 처리하였습니다.
React Redux는 여러 컴포넌트 간의 props를 효율적으로 관리하는 데 사용하는 라이브러리입니다. 사용자 데이터를 여러 컴포넌트에서 공유하고 관리하기 위해 Redux를 활용했습니다.
예시 코드
const initialState = {
name: '', // 이름
id: '', // 사용자 고유 id
phone: '', // 전화번호
// ...
};
const userData = createSlice({
name : 'userData',
initialState,
reducers: {
login(state, action) { // 로그인 정보를 저장
return action.payload;
},
logout(state) { // 초기 상태로 설정
return initialState;
},
}
})
initialState
에서 사용자 정보의 초기 상태를 정의합니다. 그 후, createSlice
함수를 사용해 사용자 정보와 관련된 Reducer를 생성합니다. reducers
에서는 login, logout 두 가지의 액션을 처리합니다. 이렇게 생성된 userData
슬라이스는 Redux store에 등록되어, 다양한 컴포넌트에서 사용자 데이터를 효과적으로 관리할 수 있게 됩니다.
React에서 페이지 전환 시 Router를 활용했습니다. 반면, React Native에서는 React Navigation 라이브러리를 통해 다른 페이지로의 이동이 가능합니다. 이를 통해, Android와 iOS에서 사용하는 제스처와 애니메이션을 사용할 수 있습니다.
예시 코드
const PostStack = () => {
const Stack = createNativeStackNavigator(); //React navigation stack
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="PostDetail" component={PostDetail}/>
<Stack.Screen name="NewPost" component={NewPost}/>
//...
</Stack.Navigator>
)
}
React Navigation을 이용해 네이티브 스택 내비게이터를 구성했습니다. 이를 통해 각의 화면은 스택에 순차적으로 쌓이며, 사용자는 앱 내에서 앞뒤로 화면을 이동할 수 있습니다.
<TouchableOpacity
onPress={() =>
navigation.navigate('PostStack', {
screen: 'PostDetail',
params: {postId: item.id},
})
}>
TouchableOpacity
컴포넌트를 누르면 'PostDetail' 넘어가는 기능을 구현하였습니다. 이 과정에서 params
를 사용해 게시글의 고유 ID(item.id
)를 파라미터로 전달합니다.
Async Storage를 사용하면 데이터를 클라이언트 기기에 Key-Value 형태로 저장할 수 있습니다. Redux와 달리 앱이 종료되어도 저장된 데이터가 유지되므로, 자동 로그인 기능 구현에 사용했습니다.
예시 코드
const setStorageData = async (data, key) => {
try {
const jsonValue = JSON.stringify(data);
await AsyncStorage.setItem(key, jsonValue); // 데이터를 JSON으로 저장합니다.
} catch (e) {
// 오류가 발생했을 때
}
};
const getStorageData = async (key) => {
try {
const jsonValue = await AsyncStorage.getItem(key); // 데이터를 비동기로 가져옵니다
return jsonValue != null ? JSON.parse(jsonValue) : null; // 가져온 JSON 데이터를 JavaSCript 객체로 변경하여 반환합니다
} catch (e) {
// 오류가 발생했을 때
}
};
setStorageData
함수는 데이터를 로컬 저장소에 저장하는 기능을 수행합니다. getStorageData
함수는 로컬 저장소에 저장된 데이터를 불러오는 기능을 담당합니다. 이 과정을 통해 사용자가 앱을 종료한 후 다시 열었을 때 로그인 상태를 유지할 수 있습니다.
Name | Method | URL | Tag |
---|---|---|---|
회원가입 | POST | /user | 회원 |
로그인 | POST | /user/sign-in | 회원 |
회원 프로필 수정 | PUT | /user/{userId}/profile | 회원 |
회원 이메일 찾기 | POST | /user/email | 회원 |
회원 비밀번호 재설정 | POST | /user/password | 회원 |
회원 탈퇴 | PATCH | /user/withdraw | 회원 |
게시글 생성 | POST | /post | 게시글 |
게시글 수정 | GUT | /post/{postId} | 게시글 |
게시글 삭제 | DELETE | /post/{postId} | 게시글 |
전체 게시글 조회 | GET | /posts | 게시글 |
게시글 상세 페이지 조회 | GET | /posts/{postId} | 게시글 |
상품 상태 수정 | PATCH | /post/{postId}/product-status | 게시글 |
게시글 상태 수정 | PATCH | /post/{postId}/status | 게시글 |
게시글 좋아요, 취소 | POST | /post/{postId}/hearts | 게시글 |
끌어올리기 | PUT | /post/{postId}/recent | 게시글 |
판매중 게시글 조회 | GET | /posts/selling | 게시글 |
숨김 게시글 조회 | GET | /posts/hide | 게시글 |
판매완료 게시글 조회 | GET | /posts/completed | 게시글 |
구매완료 게시글 조회 | GET | /posts/purchased | 게시글 |
관심 게시글 조회 | GET | /posts/likedBy | 게시글 |
게시글 검색 | GET | /posts/search | 게시글 |
카테고리별 게시글 검색 | GET | /posts/category/{category} | 게시글 |
판매중 게시글 개수 조회 | GET | /posts/selling/count | 게시글 |
숨김 게시글 개수 조회 | GET | /posts/hide/count | 게시글 |
판매완료 게시글 개수 조회 | GET | /posts/completed/count | 게시글 |
판매하기 | POST | /posts/{postId}/sell | 게시글 |
댓글 추가 | POST | /post/{postId}/comments | 댓글 |
댓글 수정 | PUT | /post/{postId}/comments/{commentId} | 댓글 |
댓글 삭제 | DELETE | /post/{postId}/comments/{commentId} | 댓글 |
후기 추가 | POST | /review/{postId} | 후기 |
후기 조회 | GET | /review/{postId} | 후기 |
구매자 후기 작성 여부 조회 | GET | /reviews/{postId}/buyer-status | 후기 |
판매자 후기 작성 여부 조회 | GET | /reviews/{postId}/seller-status | 후기 |
게시글
목록
글 작성
상세 페이지
조회
댓글
유저 관리
판매 및 구매