김성연

tjddus: add proto react_native, user_server

Showing 61 changed files with 2102 additions and 0 deletions
# Default ignored files
/workspace.xml
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/code.iml" filepath="$PROJECT_DIR$/.idea/code.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/locationTest/expo-location-example" vcs="Git" />
<mapping directory="$PROJECT_DIR$/my-project" vcs="Git" />
<mapping directory="$PROJECT_DIR$/render_server_react_native" vcs="Git" />
<mapping directory="$PROJECT_DIR$/render_server_react_native/@expo/vector-icons" vcs="Git" />
</component>
</project>
\ No newline at end of file
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}
node_modules/**/*
.expo/*
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
web-report/
# macOS
.DS_Store
import React, {useState} from 'react';
import {StyleSheet, Text, View, Image, StatusBar, AsyncStorage} from 'react-native';
import {AppLoading} from "expo";
import {Asset} from 'expo-asset';
import {Provider} from 'react-redux';
import * as Font from 'expo-font'
import {Ionicons} from "@expo/vector-icons";
import {NavigationContainer} from "@react-navigation/native";
import store from './store';
import StackNavigation from "./navigations/StackNavigation";
import {AuthProvider} from "./AuthContext";
import host from './env';
import axios from "axios";
const cacheImages = (images) => {
return images.map((image) => {
if (typeof image === 'string') {
return Image.prefetch(image);
} else {
return Asset.fromModule(image).downloadAsync();
}
})
};
const cacheFonts = (fonts) => {
return fonts.map((font) => {
return Font.loadAsync(font);
})
};
const App = () => {
const [isReady, setIsReady] = useState(false);
const [user, setUser] = useState('');
const loadAssets = async () => {
const images = cacheImages(
['https://images.unsplash.com/photo-1532278951723-545f655c97f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60']
);
const fonts = cacheFonts([Ionicons.font]);
const cookie = await AsyncStorage.getItem('cookie');
console.log('cookie', cookie);
if (cookie) {
try {
axios.defaults.headers.Cookie = cookie;
console.log('user/loadMe 요청보냄', `http://${host}:4001/user/loadMe`);
const res = await axios.get(`http://${host}:4001/user/loadMe`);
const {user} = res.data;
console.log(user);
setUser(user);
} catch (e) {
console.error(e);
}
}
return Promise.all([images, fonts]);
};
const onFinish = () => {
setIsReady(true);
};
const onError = (err) => {
console.error(err)
};
return (
<>
{isReady
?
// <Provider store={store}>
// <NavigationContainer>
// <StatusBar barstyle={'light-content'}/>
// <StackNavigation/>
// </NavigationContainer>
// </Provider>
<Provider store={store}>
<AuthProvider user={user}>
<NavigationContainer>
<StatusBar barstyle={'light-content'}/>
<StackNavigation/>
</NavigationContainer>
</AuthProvider>
</Provider>
:
<AppLoading
startAsync={loadAssets}
onFinish={onFinish}
onError={onError}
/>
}
</>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
import React, {createContext, useContext, useEffect, useState} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {LOAD_ME_SUCCESS} from "./reducers/user";
export const AuthContext = createContext({});
export const AuthProvider = (props) => {
const {children, user} = props;
const dispatch = useDispatch();
const onLoadMe = async () => {
try {
await dispatch({
type: LOAD_ME_SUCCESS,
data: {
user
}
});
} catch (e) {
console.log(e);
}
};
useEffect(() => {
onLoadMe();
console.log('AuthContext user', user);
}, [user]);
return (
<AuthContext.Provider>
{children}
</AuthContext.Provider>
)
};
\ No newline at end of file
{
"expo": {
"name": "render_server_react_native",
"slug": "render_server_react_native",
"platforms": [
"ios",
"android",
"web"
],
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
}
}
}
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
import React from 'react';
import {ActivityIndicator, View} from 'react-native';
const LoadingComponent = () => {
return (
<View style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}}>
<ActivityIndicator color={'grey'}/>
</View>
)
};
export default LoadingComponent;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, StyleSheet, TextInput, TouchableOpacity} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import styled from "styled-components";
import {useNavigation} from '@react-navigation/native';
import LoadingComponent from "../components/LoadingComponent";
import SignUpComponent from "./SignUpComponent";
const LoginButton = styled.TouchableOpacity`
align-items: center;
justify-content: center;
width: 60px;
height: 40px;
background-color: #e6e6fa;
border: 1px;
`;
const LoginComponent = () => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const {me} = useSelector(state => state.user);
const {isLoggingIn} = useSelector(state => state.user);
const onChangeEmail = (email) => {
setEmail(email)
};
const onChangePassword = (password) => {
setPassword(password);
};
const dispatch = useDispatch();
const onSubmit = async () => {
if (!email || !password) {
return
}
await dispatch({
type: LOG_IN_REQUEST,
data: {
email,
password
}
});
};
useEffect(() => {
setLoading(false);
setEmail('');
setPassword('');
}, []);
return (
<View style={styles.containerStyle}>
<TextInput
style={styles.input}
placeholder="Type here to Email!"
onChangeText={onChangeEmail}
defaultValue={email}
/>
<TextInput
style={styles.input}
placeholder="Type here to password!"
type="password"
onChangeText={onChangePassword}
/>
<LoginButton
title={'Login'}
onPress={onSubmit}>
<Text style={{color: '#696969'}}>Login</Text>
</LoginButton>
</View>
)
};
const styles = StyleSheet.create({
containerStyle: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#ecf0f1',
marginTop: 100,
},
input: {
width: 200,
height: 44,
padding: 10,
borderWidth: 1,
borderColor: '#778899',
marginBottom: 10,
}
});
export default LoginComponent;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, TextInput, TouchableOpacity, StyleSheet} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import {useNavigation} from '@react-navigation/native';
import LoadingComponent from "../components/LoadingComponent";
const MyProfileComponent = () => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const {me} = useSelector(state => state.user);
const {isLoggingIn} = useSelector(state => state.user);
const dispatch = useDispatch();
const onLogout = async () => {
await dispatch({
type: LOG_OUT_REQUEST
});
console.log('onLogout');
};
return (
<View>
<View style={styles.containerStyle}>
<Text style={styles.TextStyle}>마이페이지</Text>
<Text style={styles.TextStyle}>{me.email}</Text>
<Text style={styles.TextStyle}>{me.nickName}</Text>
<TouchableOpacity onPress={onLogout}>
<MaterialCommunityIcons color={'green'} name={'logout'} size={30}/>
</TouchableOpacity>
</View>
</View>
)
};
const styles = StyleSheet.create({
containerStyle: {
marginTop: 10,
alignItems: 'center',
justifyContent: 'center',
},
TextStyle: {
width: 200,
height: 44,
padding: 10,
borderWidth: 1,
borderColor: '#778899',
marginBottom: 10,
}
});
export default MyProfileComponent;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, TextInput, TouchableOpacity} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {SIGN_UP_REQUEST} from "../reducers/user";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import styled from "styled-components";
import {useNavigation} from '@react-navigation/native';
import LoadingComponent from "../components/LoadingComponent";
const SignUpButton = styled.TouchableOpacity`
align-items: center;
justify-content: center;
width: 60px;
height: 60px;
background-color: #ffffff;
border: 1px;
`;
const SignUpComponent = () => {
const navigation = useNavigation();
const [email, setEmail] = useState('');
const [nickName, setNickName] = useState('');
const [password, setPassword] = useState('');
const {me} = useSelector(state => state.user);
const {isSigningUp} = useSelector(state => state.user);
const onChangeEmail = (email) => {
setEmail(email)
};
const onChangePassword = (password) => {
setPassword(password);
};
const onChangeNickName = (nickName) => {
setNickName(nickName)
};
const dispatch = useDispatch();
const onSubmit = async () => {
await dispatch({
type: SIGN_UP_REQUEST,
data: {
email,
nickName,
password
}
});
};
return (
<>
<View>
<View>
<TextInput
style={{height: 40, marginLeft: 10}}
placeholder="Type here to Email!"
onChangeText={onChangeEmail}
/>
</View>
<View>
<TextInput
style={{height: 40, marginLeft: 10}}
placeholder="Type here to nickname!"
onChangeText={onChangeNickName}
/>
</View>
<View>
<TextInput
style={{height: 40, marginLeft: 10}}
placeholder="Type here to password!"
type="password"
onChangeText={onChangePassword}
/>
</View>
<SignUpButton title={'회원가입'} onPress={onSubmit}>
<Text>회원가입</Text>
</SignUpButton>
</View>
</>
)
};
export default SignUpComponent;
\ No newline at end of file
import {Dimensions} from 'react-native';
const {width, height} = Dimensions.get('screen');
export default{
width,
height
}
\ No newline at end of file
const host = '172.20.10.3';
export default host;
\ No newline at end of file
import React, {userLayoutEffect} from 'react';
import {createStackNavigator} from "@react-navigation/stack";
import TabNavigation from "./TabNavigation";
import SelectOrTakePhotoTabNavigation from "./SelectOrTakePhotoTabNavigation";
import UploadPhoto from "../screens/UploadPhoto";
const Stack = createStackNavigator();
const SelectOrTakePhotoStackNavigation = () =>{
return (
<Stack.Navigator
mode='card'
>
<Stack.Screen
name='SelectOrTakePhotoTabNavigation'
component={SelectOrTakePhotoTabNavigation}
/>
<Stack.Screen
name='UploadPhoto'
component={UploadPhoto}
/>
</Stack.Navigator>
)
};
export default SelectOrTakePhotoStackNavigation;
\ No newline at end of file
import React, {useLayoutEffect} from 'react';
import {Ionicons} from "@expo/vector-icons";
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import TakePhoto from "../screens/TakePhoto";
import SelectPhoto from "../screens/SelectPhoto";
const Tab = createBottomTabNavigator();
const SelectOrTakePhotoTabNavigation = (props) => {
const {navigation, route} = props;
// useLayoutEffect(() => {}, [route]);
return (
<Tab.Navigator
tabBarOptions = {{}}
>
<Tab.Screen
name='SelectPhoto'
component={SelectPhoto}
/>
<Tab.Screen
name='TakePhoto'
component={TakePhoto}
/>
</Tab.Navigator>
)
};
export default SelectOrTakePhotoTabNavigation;
\ No newline at end of file
import React from 'react';
import {createStackNavigator} from "@react-navigation/stack";
import Gallery from "../screens/Gallery";
import Main from "../screens/Main";
import TabNavigation from "./TabNavigation";
import {TouchableOpacity} from "react-native";
// import * as WebBrowser from 'expo-web-browser';
import {Ionicons} from "@expo/vector-icons";
import SelectOrTakePhotoStackNavigation from "./SelectOrTakePhotoStackNavigation";
const Stack = createStackNavigator();
//
// const openBrowser = (url) => async () => {
// await WebBrowser.openBrowserAsync(url);
// };
const StackNavigation = () =>{
return (
<Stack.Navigator
mode='card'
screenOptions = {{
headerRight: () => {
return (
<TouchableOpacity>
<Ionicons name={'logo-youtube'} color={'red'} size={25}/>
</TouchableOpacity>
)
}
}}
>
<Stack.Screen
name='TabNavigation'
component={TabNavigation}
/>
<Stack.Screen
name='SelectOrTakePhotoStackNavigation'
component={SelectOrTakePhotoStackNavigation}
/>
<Stack.Screen
name='Gallery'
component={Gallery}
/>
</Stack.Navigator>
)
};
export default StackNavigation;
\ No newline at end of file
import React, {useLayoutEffect} from 'react';
import {Ionicons} from "@expo/vector-icons";
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import Main from "../screens/Main";
import Login from "../screens/Login";
import Profile from "../screens/Profile";
import Maps from "../screens/Maps";
const Tab = createBottomTabNavigator();
const getHeaderName = (route) => {
};
const TabNavigation = (props) => {
const {navigation, route} = props;
// useLayoutEffect(() => {}, [route]);
return (
<Tab.Navigator
// screenOptions = {({route})=>{}}
tabBarOptions = {{}}
>
<Tab.Screen
name='main'
component={Main}
/>
<Tab.Screen
name='login'
component={Login}
/>
<Tab.Screen
name='maps'
component={Maps}
/>
<Tab.Screen
name='Profile'
component={Profile}
/>
</Tab.Navigator>
)
};
export default TabNavigation;
\ No newline at end of file
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@react-native-community/masked-view": "0.1.6",
"@react-navigation/bottom-tabs": "^5.4.1",
"@react-navigation/native": "^5.3.0",
"@react-navigation/stack": "^5.2.11",
"axios": "^0.19.2",
"expo": "~37.0.3",
"expo-asset": "^8.1.4",
"expo-camera": "~8.2.0",
"expo-font": "^8.1.1",
"expo-media-library": "~8.1.0",
"expo-permissions": "~8.1.0",
"expo-web-browser": "^8.2.1",
"react": "~16.9.0",
"react-dom": "~16.9.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
"react-native-gesture-handler": "~1.6.0",
"react-native-maps": "0.26.1",
"react-native-reanimated": "~1.7.0",
"react-native-safe-area-context": "0.7.3",
"react-native-screens": "~2.2.0",
"react-native-web": "~0.11.7",
"react-redux": "^7.2.0",
"redux": "^4.0.5",
"redux-saga": "^1.1.3",
"styled-components": "^5.1.0"
},
"devDependencies": {
"@babel/core": "^7.8.6",
"@expo/vector-icons": "^10.0.6",
"babel-preset-expo": "~8.1.0"
},
"private": true
}
import {combineReducers} from "redux";
import user from './user';
// import post from './post';
const rootReducer = combineReducers({
user,
// post
});
export default rootReducer;
\ No newline at end of file
export const initialState = {
me: null,
isLoggingIn: false,
isSigningUp: false,
isLoadingMe: false,
isLoggingOut: false,
info: '',
};
export const LOG_IN_REQUEST = 'LOG_IN_REQUEST';
export const LOG_IN_SUCCESS = 'LOG_IN_SUCCESS';
export const LOG_IN_FAILURE = 'LOG_IN_FAILURE';
export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
export const SIGN_UP_SUCCESS = 'SIGN_UP_SUCCESS';
export const SIGN_UP_FAILURE = 'SIGN_UP_FAILURE';
export const LOAD_ME_REQUEST = 'LOAD_USER_REQUEST';
export const LOAD_ME_SUCCESS = 'LOAD_USER_SUCCESS';
export const LOAD_ME_FAILURE = 'LOAD_USER_FAILURE';
export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
export const LOG_OUT_SUCCESS = 'LOG_OUT_SUCCESS';
export const LOG_OUT_FAILURE = 'LOG_OUT_FAILURE';
export default (state = initialState, action) => {
switch (action.type) {
case LOG_IN_REQUEST: {
return {
...state,
isLoggingIn: true,
}
}
case LOG_IN_SUCCESS: {
const {me} = action.data;
return {
...state,
me,
isLoggingIn: false,
};
}
case LOG_IN_FAILURE: {
const {info} = action.data;
return {
...state,
isLoggingIn: false,
info,
}
}
case SIGN_UP_REQUEST: {
return {
...state,
isSigningUp: true
}
}
case SIGN_UP_SUCCESS: {
const {me} = action.data;
return {
...state,
me,
isSigningUp: false,
};
}
case SIGN_UP_FAILURE: {
const {info} = action.data;
return {
...state,
isSigningUp: false,
info
};
}
case LOAD_ME_REQUEST: {
return {
...state,
isLoadingMe: true
}
}
case LOAD_ME_SUCCESS: {
const {user} = action.data;
console.log(user);
return {
...state,
me: user,
isLoadingMe: false
}
}
case LOAD_ME_FAILURE: {
const {info} = action.data;
return {
...state,
isLoadingMe: false,
info
}
}
case LOG_OUT_REQUEST: {
return {
...state,
isLoggingOut: true
}
}
case LOG_OUT_SUCCESS: {
console.log('LOG_OUT_SUCCESS 완료');
return {
...state,
me: null,
isLoggingOut: false
}
}
case LOG_OUT_FAILURE: {
const {info} = action.data;
return {
...state,
isLoggingOut: false,
info
}
}
default: {
return {
...state,
};
}
}
};
\ No newline at end of file
import {all, fork} from 'redux-saga/effects';
import user from './user';
// import post from './post';
export default function* rootSaga(){
yield all([
fork(user),
// fork(post)
])
}
\ No newline at end of file
import {all, call, fork, delay, put, takeEvery, takeLatest} from 'redux-saga/effects';
import axios from 'axios';
import host from '../env';
import {
LOG_IN_FAILURE,
LOG_IN_REQUEST,
LOG_IN_SUCCESS,
SIGN_UP_FAILURE,
SIGN_UP_REQUEST,
SIGN_UP_SUCCESS,
LOAD_ME_REQUEST,
LOAD_ME_SUCCESS,
LOAD_ME_FAILURE,
LOG_OUT_REQUEST,
LOG_OUT_SUCCESS,
LOG_OUT_FAILURE,
} from '../reducers/user';
import {AsyncStorage} from 'react-native';
const parseCookies = (cookies = '') =>
cookies
.split(';')
.map(v =>
v.split('=')
)
.reduce((acc, [key, value]) => {
acc[key.trim()] = decodeURIComponent(value);
console.log(acc);
return acc;
}, {});
//로그인
function loginAPI(data) {
const {email, password} = data;
console.log(email, password);
console.log(`http://${host}:4001/user/login`);
return axios.post(`http://${host}:4001/user/login`, {
email, password
}, {
withCredentials: true
});
}
// # 함수의 동기적인 호출을 할 때 사용
// 응답이 다 받아진 후에 실행할 때 사용
function* login(action) {
try {
console.log('login하러 왔어요');
const res = yield call(loginAPI, action.data);
console.log('서버 login에서 온 응답', res);
const {user} = res.data;
const cookieArray = res.headers['set-cookie'];
console.log(cookieArray);
yield call(AsyncStorage.setItem, 'cookie', `userChecker=s%3A${cookieArray.map(cookie => parseCookies(cookie)['userChecker'].substring(2))}`);
yield put({
type: LOG_IN_SUCCESS,
data: {
me: user
}
})
} catch (e) {
console.error(e);
yield put({
type: LOG_IN_FAILURE,
data: {
info: e.response.data.info
}
})
}
};
function* watchLogin() {
yield takeLatest(LOG_IN_REQUEST, login);
}
// 회원가입
function signUpAPI(data) {
const {email, nickName, password} = data;
return axios.post(`http://${host}:4001/user/signUp`, {
email, nickName, password
}, {
withCredentials: true
});
}
function* signUp(action) {
try {
console.log('signUp 실행원할');
const res = yield call(signUpAPI, action.data);
const {me} = res.data;
yield put({
type: SIGN_UP_SUCCESS,
data: {
me
}
});
} catch (e) {
console.error(e);
yield put({
type: SIGN_UP_FAILURE,
data: {
info: e.response.data.info
}
});
}
}
// # generator 함수에서 마지막 액션 하나만 유효하다고 인정
// 실수로 회원가입 버튼을 연달아 누를 경우 서버의 요청이 2번 가지 않게함
function* watchSignUp() {
yield takeLatest(SIGN_UP_REQUEST, signUp);
}
function loadMeAPI() {
return axios.get(`http://${host}:4001/user/loadMe`, {withCredentials: true})
};
function* loadMe(action) {
try {
console.log('loadMe 실행원할');
const res = yield call(loadMeAPI, action.data);
const {user} = res.data;
yield put({
type: LOAD_ME_SUCCESS,
data: {
user
}
})
} catch (e) {
console.error(e);
yield put({
type: LOAD_ME_FAILURE,
data: {
info: e.response.data.info
}
})
}
};
function* watchLoadMe() {
yield takeLatest(LOAD_ME_REQUEST, loadMe);
}
function logoutAPI() {
return axios.get(`http://${host}:4001/user/logout`, {withCredentials: true});
}
function* logout() {
try {
const res = yield call(logoutAPI);
console.log('logout 완료');
yield call(AsyncStorage.removeItem, 'cookie');
yield put({
type: LOG_OUT_SUCCESS
});
} catch (e) {
console.error(e);
yield put({
type: LOG_OUT_FAILURE,
data: {
info: e.response.data.info
}
})
}
}
function* watchLogoutMe() {
yield takeLatest(LOG_OUT_REQUEST, logout);
}
// # 모든 액션을 유효하게 인정한다.
// while(true)로 감싸는 효과
// takeEvery
// # 함수의 비동기적인 호출을 사용할 때
// call과 다르게 fork는 순서 상관없이 실행할 때 사용
export default function* userSaga() {
yield all([
fork(watchLogin),
fork(watchSignUp),
fork(watchLoadMe),
fork(watchLogoutMe),
]);
}
import React from 'react';
import {View, Text, Button} from 'react-native';
import {useNavigation} from "@react-navigation/native";
const Gallery = () => {
const navigation = useNavigation();
return (
<View>
<Text>
하이하이
</Text>
</View>
)
};
export default Gallery;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, TextInput, TouchableOpacity} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import styled from "styled-components";
import {useNavigation} from '@react-navigation/native';
import LoadingComponent from "../components/LoadingComponent";
import MyProfileComponent from "../components/MyProfileComponent";
import LoginComponent from "../components/LoginComponent";
const Login = () => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const {me} = useSelector(state => state.user);
const {isLoggingIn} = useSelector(state => state.user);
useEffect(() => {
setLoading(false);
}, [me]);
return (
<View>
{me ?
<MyProfileComponent/>
:
<LoginComponent/>
}
</View>
)
};
export default Login;
\ No newline at end of file
import React from 'react';
import {View, Text, TouchableOpacity} from 'react-native';
import styled from "styled-components";
import {useNavigation} from "@react-navigation/native";
import {useSelector} from "react-redux";
const GoToGalleryButton = styled.TouchableOpacity`
width: 60px;
border: 1px;
align-items: center;
justify-content: center;
flex: 2
`;
const GoToSelectOrTakePhotoButton = styled.TouchableOpacity`
position: absolute;
right: 20px;
bottom: 20px;
width: 30px;
height: 30px;
border-radius: 50px;
border: 15px solid green;
`;
const Main = () => {
const navigation = useNavigation();
const goToGallery = () => {
console.log(navigation.navigate);
navigation.navigate('Gallery');
};
const goToSelectOrTakePhoto = () => {
navigation.navigate('SelectOrTakePhotoStackNavigation');
};
const {me} = useSelector(state => state.user);
return (
<>
<View style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}}>
<GoToGalleryButton title={'갤러리로 가보자'} onPress={goToGallery}>
<Text>갤러리로 가보자</Text>
</GoToGalleryButton>
<View style={{
flex: 8,
flexDirection: 'row'
}}>
<Text style={{
width: '100%',
flex: 1,
backgroundColor: 'red'
}}>메인페이지</Text>
<Text style={{
width: '100%',
flex: 1,
backgroundColor: 'grey',
}}>메인페이지2</Text>
<GoToSelectOrTakePhotoButton onPress={goToSelectOrTakePhoto}/>
</View>
</View>
</>
)
};
export default Main;
\ No newline at end of file
import React from 'react';
import MapView from 'react-native-maps';
import {StyleSheet, Text, View, Dimensions} from 'react-native';
import screen from '../constants/layout';
const Maps = () => {
return (
<View style={styles.container}>
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421
}}
/>
</View>
)
};
export default Maps;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
},
mapStyle: {
width: screen.width,
height: screen.height / 2,
},
});
import React from 'react';
import {View, Text, Button} from 'react-native';
import SignUpComponent from "../components/SignUpComponent";
const Profile = () => {
const {me} = (state => state.user);
return (
<View>
<SignUpComponent/>
</View>
)
};
export default Profile;
\ No newline at end of file
import React, {useEffect, useState} from 'react';
import * as MediaLibrary from 'expo-media-library';
import * as Permission from 'expo-permissions';
import {Image, ImageBackground, ScrollView, View, Text, TouchableOpacity} from "react-native";
import {useNavigation} from '@react-navigation/native';
import LoadingComponent from "../components/LoadingComponent";
import screen from '../constants/layout';
import {UploadPhoto} from './UploadPhoto';
const SelectPhoto = (props) => {
const navigation = useNavigation();
const [loading, setLoading] = useState(false);
const [hasPermission, setHasPermission] = useState(true);
const [selectedPhoto, setSelectedPhoto] = useState([]);
const [allPhotos, setAllPhotos] = useState([]);
const getPhotos = async () => {
try {
const {assets} = await MediaLibrary.getAssetsAsync();
const [firstPhoto] = assets;
setSelectedPhoto([firstPhoto]);
setAllPhotos(assets);
} catch (e) {
console.error(e)
} finally {
setLoading(false);
}
};
const askPermission = async () => {
try {
setLoading(true);
const {status} = await Permission.askAsync(Permission.CAMERA_ROLL);
console.log(status);
if (status === 'granted') {
setHasPermission(true);
await getPhotos();
}
} catch (e) {
console.error(e);
setHasPermission(false);
}
};
const changeSelectedPhoto = (photo) => {
setSelectedPhoto([photo]);
};
const uploadPhoto = () => {
navigation.navigate('UploadPhoto', {photos: selectedPhoto})
};
useEffect(() => {
askPermission();
return () => {
setLoading(true);
setHasPermission(false);
setSelectedPhoto([]);
setAllPhotos([]);
}
}, []);
return (
<View>
{loading
?
<LoadingComponent/>
:
hasPermission
?
selectedPhoto[0]
?
<>
<ImageBackground
style={{width: screen.width, height: screen.height / 2}}
source={{uri: selectedPhoto[0].uri}}
>
<TouchableOpacity
style={{
justifyContent: 'center',
alignItems: 'center',
}}
key={selectedPhoto.id}
onPress={uploadPhoto}
>
<Text>선택</Text>
</TouchableOpacity>
</ImageBackground>
<ScrollView>
<>
<ScrollView horizontal contentContainerStyle={{flexDirection: 'row'}}>
{allPhotos.map(photo => {
return (
<TouchableOpacity
key={photo.id}
onPress={() => changeSelectedPhoto(photo)}>
<Image
source={{uri: photo.uri}}
style={{
width: screen.width / 3,
height: screen.height / 4,
opacity: photo.id === selectedPhoto[0].id ? 0.6 : 1
}}/>
</TouchableOpacity>
)
}
)}
</ScrollView>
</>
</ScrollView>
</>
:
null
:
<Text>사용자 권한이 없습니다</Text>
}
</View>
)
};
export default SelectPhoto;
\ No newline at end of file
import React, {useEffect, useState, useRef} from 'react';
import * as MediaLibrary from 'expo-media-library';
import * as Permission from 'expo-permissions';
import {Image, ImageBackground, ScrollView, View, Text, TouchableOpacity} from "react-native";
import {useNavigation} from '@react-navigation/native';
import LoadingComponent from "../components/LoadingComponent";
import screen from '../constants/layout';
import { Camera } from 'expo-camera';
import styled from "styled-components";
import {MaterialCommunityIcons} from "@expo/vector-icons";
const TakePhotoButton = styled.TouchableOpacity`
width: 70px;
height: 70px;
border-radius: 50px;
border: 15px solid green;
`;
const TakePhoto = (props) => {
const navigation = useNavigation();
const [loading, setLoading] = useState(false);
const [hasPermission, setHasPermission] = useState(false);
const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
const [canTakePhoto, setCanTakePhoto] = useState(true);
const cameraRef = useRef(null);
const askPermission = async () => {
try {
setLoading(true);
const {status} = await Permission.askAsync(Permission.CAMERA);
console.log(status);
if (status === 'granted') {
setHasPermission(true);
}
} catch (e) {
console.error(e);
setHasPermission(false);
} finally {
setLoading(false)
}
};
const changeCameraType = () => {
if (cameraType === Camera.Constants.Type.front) {
setCameraType(Camera.Constants.Type.back);
} else {
setCameraType(Camera.Constants.Type.front);
}
};
const takePhoto = async () => {
if (!canTakePhoto) {
return
}
try {
setCanTakePhoto(false);
const {uri} = await cameraRef.current.takePictureAsync({quality: 1});
const asset = await MediaLibrary.createAssetAsync(uri);
navigation.navigate('UploadPhoto', {photo: asset});
} catch (e) {
console.error(e);
setCanTakePhoto(true);
}
};
const goUpload = () => {
navigation.navigate('UploadPhoto');
};
useEffect(() => {
askPermission();
}, []);
return (
<View style={{alignItems: 'center'}}>
{loading
? <LoadingComponent/>
: hasPermission ?
<View>
<Camera
ref={cameraRef}
type={cameraType}
style={{
justifyContent: 'flex-end',
padding: 10,
width: screen.width,
height: screen.height / 2
}}>
<TouchableOpacity onPress={changeCameraType}>
<MaterialCommunityIcons color={'green'} name={'camera'} size={24}/>
</TouchableOpacity>
</Camera>
<TakePhotoButton
onPress={takePhoto}
disabled={!canTakePhoto}
/>
<TakePhotoButton
onPress={goUpload}
/>
</View>
:
null
}
</View>
)
};
export default TakePhoto;
\ No newline at end of file
import React from 'react';
import {View, Text, Image, Button, StyleSheet} from 'react-native';
import styled from "styled-components";
const UploadPhoto = (props) => {
const {route} = props;
const {photos} = route.params;
return (
<View>
<Text>
하이하이
</Text>
<View>{photos.map((photo, index) => {
return (
<Image style={{width: 200, height: 200}} source={{uri: photo.uri}} key={photo.id}/>)
})}</View>
</View>
)
}
export default UploadPhoto;
\ No newline at end of file
import createSagaMiddleware from "redux-saga";
import {applyMiddleware, compose, createStore} from "redux";
import rootReducer from "./reducers";
import rootSaga from "./sagas";
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const enhancer = compose(
applyMiddleware(...middlewares),
// !options.isServer && typeof window.REDUX_DEVTOOLS_EXTENSION !== 'undefined' ? window.REDUX_DEVTOOLS_EXTENSION() : (f) => f,
);
const store = createStore(rootReducer, enhancer);
sagaMiddleware.run(rootSaga);
export default store;
\ No newline at end of file
This diff could not be displayed because it is too large.
PORT=4001
COOKIE_SECRET=test
\ No newline at end of file
# Default ignored files
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/user_and_post_server.iml" filepath="$PROJECT_DIR$/.idea/user_and_post_server.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>
\ No newline at end of file
const path = require('path');
const morgan = require('morgan');
const express = require('express');
//header Cookie : 쿠키, 해쉬함수
//오리지널 >> 해쉬 >> 압축메세지
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const passport = require('passport');
const httpErrors = require('http-errors');
const dotenv = require('dotenv');
dotenv.config();
const MongoStore = require('connect-mongo')(expressSession);
const MONGO_URL = `mongodb://localhost:27017/admin`;
const cors = require('cors');
const {sequelize} = require('./models/index');
sequelize.sync({force: false});
const connect = require('./schemas/index');
connect();
const sessionMiddleware = expressSession({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false
},
name: 'userChecker',
store: new MongoStore({
url: MONGO_URL,
collection: "sessions"
}),
});
const passportIndex = require('./passport/index');
const userRouter = require('./routes/user');
passportIndex(passport);
// const app = express(); 사용 설명서
// app.use(미들웨어)
// 미들웨어란: (req, res, next) => {req와 res를 분석 및 가공, next로 req와 res를 다음 미들웨어로 전달}
// 따라서 미들웨어끼리의 순서가 중요하다
// 어떤 미들웨어에서 req에 변수를 등록하고, 다음 미들웨어에서 그 변수를 가져다가 사용하는 방법
// 1. req.set()으로 변수를 등록하고, req.get()으로 전역 변수들을 가져와서 사용할 수 있다
// 2. app.set()으로 변수를 등록하고, req.app.get()으로 전역 변수들을 가져와서 사용할 수 있다(req 객체에 app 객체는 자동으로 세팅된다)
// 3. req.app.set()으로 변수를 등록하고, req.app.get()으로 전역 변수들을 가져와서 사용할 수 있다
// res 사용법
// 오리지날: res.writeHead, res.write, res.end
// 익스프레스 프레임워크: res.render('view 파일 이름', 자바스크립트 변수 객체), res.send(아무거나), res,json(객체), res.redirect('경로'), res.sendFile,
const app = express(); // 익스프레스 프레임워크를 사용하기 위한 app 객체를 생성
app.use(morgan('dev')); // 로거를 미들웨어 최상단에 위치시켜서 서버로 들어오는 모든 요청에 대한 로그를 콘솔에서 확인
app.use(cors({
origin: 'http://localhost:3001',
credentials: true
}));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(sessionMiddleware);
app.use(passport.initialize()); // 패스포트 작동 시작
app.use(passport.session()); // 패스포트 세션 작업
app.use('/public', express.static(path.join(__dirname, 'open'))); // 모두에게 공개된 폴더 설정
app.use('/user', userRouter);
app.use(function (req, res, next) {
next(httpErrors(404));
});
app.use(function (err, req, res, next) {
// set locals, only providing error in development
console.log(req);
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
//render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = {
app,
sessionMiddleware
};
\ No newline at end of file
#!/usr/bin/env node
/*
* Module dependencies
*/
const debug = require('debug')('node_study_project_final:server');
const http = require('http');
const {app} = require('../app');
/*
* Get port from environment and store in Express.
*/
const port = normalizePort(process.env.PORT || '3001');
app.set('port', port);
/*
* Create HTTP server.
*/
const server = http.createServer(app);
function normalizePort(val) {
const port = parseInt(val, 10);
if (isNaN(port)) {
return val;
}
if (port >= 0) {
return port;
}
return false;
};
const onListening = () => {
const addr = server.address();
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
};
const onError = (error) => {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
};
server.listen(port);
server.on('listening', onListening);
server.on('error', onError);
\ No newline at end of file
{
"development": {
"username": "root",
"password": "ksy98042!",
"database": "capstone_design_prj1",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases": false
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases": false
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases": false
}
}
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const models = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
// 반복문을 돌면서 models 내에 있는 파일들을 읽고 그것을 모델로 정의함
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = sequelize['import'](path.join(__dirname, file));
models[model.name] = model;
});
models.User = require('./user')(sequelize, Sequelize);
Object.keys(models).forEach(modelName => {
if (models[modelName].associate) {
models[modelName].associate(models);
}
});
models.sequelize = sequelize;
models.Sequelize = Sequelize;
module.exports = models;
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define("User", {
email: {
type: DataTypes.STRING(30),
allowNull: false,
unique: true
},
nickName: {
type: DataTypes.STRING(10),
allowNull: false,
unique: true
},
hashedPassword: {
type: DataTypes.STRING(200),
allowNull: false
}
}, {
timestamps: true,
paranoid: true,
underscored: false,
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci'
});
// User.associate = (models) => {
// models.User.hasMany(models.SnsId, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
// models.User.hasMany(models.Post, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
// models.User.hasMany(models.Comment, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
// };
return User;
}
\ No newline at end of file
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
.card {
float: left;
margin: 10px;
border: 3px solid #e3e3e3;
width: 300px;
}
.post-img {
width: 300px;
}
.mine {
background-color: #808B96 ;
}
.other {
background-color: #BFC9CA;
}
.system {
text-align: center;
}
This diff could not be displayed because it is too large.
{
"name": "capstone_design_prj1",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "nodemon ./bin/www"
},
"dependencies": {
"axios": "^0.19.2",
"bcrypt": "^3.0.6",
"connect-mongo": "^3.2.0",
"cookie-parser": "~1.4.4",
"cookie-signature": "^1.1.0",
"cors": "^2.8.5",
"debug": "~2.6.9",
"dotenv": "^8.1.0",
"express": "~4.16.1",
"express-session": "^1.16.2",
"fs": "0.0.1-security",
"http-errors": "~1.6.3",
"mongoose": "^5.9.2",
"morgan": "^1.9.1",
"multer": "^1.4.2",
"mysql": "^2.18.1",
"mysql2": "^1.7.0",
"nodemon": "^1.19.4",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"path": "^0.12.7",
"pug": "2.0.0-beta11",
"sequelize": "^5.21.5",
"sequelize-cli": "^5.5.1",
"socket.io": "^2.3.0"
}
}
const models = require('../models/index');
const localStrategy = require('./localStrategy');
module.exports = (passport) => {
passport.serializeUser((user, done) => {
done(null, user.email);
});
passport.deserializeUser(async (email, done) => {
try {
const user = await models.User.findOne({
where: {email},
attributes: ['id', 'email', 'nickName']
});
if (!user) {
console.error('유저 데이터가 존재하지 않습니다.');
done(null, false, {message: '유저 데이터가 존재하지 않습니다.'});
}
return done(null, user);
} catch (e) {
console.error(e);
done(e);
}
});
localStrategy(passport);
};
const LocalStrategy = require('passport-local').Strategy;
const models = require('../models/index');
const bcrypt = require("bcrypt");
module.exports = (passport) => {
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
}, async (email, password, done) => {
try {
let user = await models.User.findOne({
where: {email}
});
if (!user) {
return done(null, false, {message: "유저 데이터가 존재하지 않습니다."});
}
let resultOfPasswordCheck = await bcrypt.compare(password, user.hashedPassword);
if (!resultOfPasswordCheck) {
return done(null, false, {message: '비밀번호 에러입니다'});
}
user = await models.User.findOne({
where:{email},
attributes: ['id', 'email', 'nickName']
});
return done(null, user);
} catch (e) {
console.error(e);
return done(e);
}
})
);
};
\ No newline at end of file
const express = require('express');
const router = express.Router();
const models = require('../models/index');
router.get('/', async (req, res, next) => {
try {
if (!req.isAuthenticated()) {
return res.render('index', {
title: '홈',
user: null,
posts: [],
}
);
}
return res.render('index', {
title: '홈',
user: req.user,
}
);
} catch (e) {
console.error(e);
next(e);
}
});
module.exports = router;
\ No newline at end of file
const isLoggedIn = (req, res, next) => {
if (req.isAuthenticated()) {
next();
} else {
res.redirect('/');
}
};
let isNotLoggedIn = (req, res, next) => {
if (!req.isAuthenticated()) {
next();
} else {
res.redirect('/');
}
};
module.exports = {
isLoggedIn,
isNotLoggedIn,
};
\ No newline at end of file
const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const passport = require('passport');
const {isLoggedIn, isNotLoggedIn} = require("./middleware");
const models = require('../models/index');
router.get('/loadMe', isLoggedIn, (req, res, next) => {
console.log('loadMe요청옴', req.user);
return res.json({user: req.user});
});
router.get('/signUp', isNotLoggedIn, (req, res, next) => {
return res.render('SignUpComponent.vue', {
title: '회원가입'
});
});
router.post('/signUp', isNotLoggedIn, async (req, res, next) => {
let {email, nickName, password} = req.body;
try {
let user = await models.User.findOne({
where: {email}
});
if (user) {
return res.json({user});
}
const hashedPassword = await bcrypt.hash(password, 10);
const signupComplete = await models.User.create({
email, nickName, hashedPassword
});
user = await models.User.findOne({
where: {email},
attributes: ['id', 'email', 'nickName']
});
return req.login(user, (err) => {
if (err) {
console.error(err);
return next(err);
}
return res.json({me: user});
});
} catch (e) {
console.error(e);
next(e);
}
});
router.post('/login', isNotLoggedIn, (req, res, next) => {
passport.authenticate('local', {}, (err, user, info) => {
if (err) {
console.error(err);
return next(err);
}
if (info) {
console.error(info.message);
return res.status(401).send(info.message);
}
req.login(user, (err) => {
if (err) {
console.error(err);
return next(err);
}
///////////////////////// req.session.returnURL
// nuxt
// return res.json({user: req.user});
return res.json({user: req.user});
});
})(req, res, next);
});
router.get('/profile', isLoggedIn, (req, res, next) => {
return res.render('profile', {title: '프로필', user: req.user});
});
router.post('/updateProfile', isLoggedIn, async (req, res, next) => {
let {newNickName} = req.body;
await models.User.update({
nickName: newNickName
}, {
where: {email: req.user.email}
});
let user = await models.User.findOne({
where: {email: req.user.email}
});
if (!user) {
return res.redirect('/');
}
return res.render('profile', {
title: 'profile',
user
})
});
router.get('/deleteProfile', async (req, res, next) => {
let email = {email: req.user.email};
let User = await models.User.destroy({
where: {email}
});
return res.redirect('/');
});
router.get('/logout', (req, res, next) => {
console.log('로그아웃 요청이 들어옴');
req.logout();
req.session.destroy();
return res.send();
});
module.exports = router;
\ No newline at end of file
const mongoose = require('mongoose');
const {MONGO_ID, MONGO_PASSWORD, NODE_ENV} = process.env;
const MONGO_URL = `mongodb://localhost:27017/admin`;
const connect = () => {
if (process.env.NODE_VIEW !== 'production') {
mongoose.set('debug', true);
}
mongoose.connect(MONGO_URL, {
dbName: 'chat',
useUnifiedTopology: true
}, (err) => {
if (err) {
console.error('몽고디비 연결 에러', err);
} else {
console.log('몽고디비 연결 성공');
}
});
};
module.exports = () => {
connect();
mongoose.connection.on('error', (err) => {
console.log('연결 종료');
});
mongoose.connection.on('disconnected', (err) => {
console.error('연결이 끊어졌습니다. 재접속 시도중');
connect();
});
};
// 몽고디비는 데이터의 형식조건에서 자유롭다
// json객체 형태라면 무엇이든 저장이 가능하다
// 이러한 자유도에 제약을 걸고(형태에 제약) 안정성을 높이는 몽구스를 사용할 수 있다
// 몽고디비는 sql이 아닌 자바스크립트를 쓰기 때문에 노드와 궁합이 좋다
// 마이에스큐엘도 시퀄라이즈를 쓰면 자바스크립트로 제어할 수는 있다
// 몽고디비서버 실행 명령어: mongod --dbpath C:\Users\kimseoyoung\mongodb_data --auth
\ No newline at end of file
This diff is collapsed. Click to expand it.