1. 화면 레이아웃 잡기

웹페이지의 레이아웃을 잡기 위해 Figma로 간단히 프로토타입을 그려보았다.

 

전체적인 색감이 정해진건 아닌데 프로토타입은 핑크-보라 계열로 그려봤다.

 

레이아웃은 크게 상단바, 사이드바, 본문 영역으로 나뉜다.

 

  • 상단바 왼쪽에는 사이트 이름(미정), 오른쪽에는 탭 메뉴가 있다.
  • 사이드바에는 사용자 프로필과 친구 목록이 나오도록 하여 친구 이름을 클릭했을 때 해당 사용자와 채팅하는 화면으로 넘어가는 것을 생각했다.
  • 상단바 메뉴를 클릭했을 때 클릭한 메뉴에 따라 본문 영역의 내용이 바뀌도록 한다.

 

프로토타입을 바탕으로 친구 검색 화면, 채팅 화면 레이아웃을 구현했다.

 

친구 검색 화면
채팅 화면

우선은 기능 구현을 위해 각 영역만 잡고 디테일한 CSS는 나중에 수정할 것이다.

 

 

2. 친구 검색하기

채팅을 할 친구를 추가하면 사이드바 친구 목록에 추가될 것이고, 그 친구에게 채팅을 보낼 수 있다.

따라서 아이디를 이용해서 다른 사용자를 검색하는 기능을 구현했다.

사용자 정보는 Firestore에 저장되어 있으므로 forEach문으로 조회한다.

 

    const friendId = props.friendId;
    const [isLoaded, setLoaded] = React.useState(false);
    const [friendName, setFriendName] = React.useState('');

    //firebase firestore에서 해당 유저 검색
    const findUser = () => {
        firestore.collection('users').get().then((docs) => {
            docs.forEach((doc) => {
                if (doc.data().id == friendId) {
                    setFriendName(doc.data().name);
                    setLoaded(true);
                }
            });
            setLoaded(true);
        })
    }

 

상위 컴포넌트에서 props로 받아온 아이디를 검색하는 코드이다.

 

 

해당 아이디와 일치하는 사용자가 있으면 화면에 나타난다.

현재는 테스트 편의성을 위해 자신의 아이디도 검색 가능하도록 했다.

 

 

3. 사용자 정보 Redux로 관리하기

로그인 이후부터는 로그인한 사용자 정보가 컴포넌트 곳곳에서 사용될 것인데,

이를 위해서는 전역 상태 관리가 필요하다고 느꼈다.

이것을 위해 Redux(리덕스)를 이용하여 로그인한 사용자 정보를 관리하도록 구조를 변경했다.

 

configStore.js

import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import user from './modules/user';
import { createBrowserHistory } from 'history';

export const history = createBrowserHistory();

const middlewares = [thunk];

const enhancer = applyMiddleware(...middlewares);
const rootReducer = combineReducers({user});
const store = createStore(rootReducer, enhancer);

export default store;

 

user.js

import {firestore} from '../../services/firebase';

const user_db = firestore.collection('users');

//Actions
const GET_USER = 'user/GET_USER';
const IS_LOADED = 'user/IS_LOADED';

const initialState = {
    email: '',
    id: '',
    name: '',
    is_loaded: false,
}

//Action Creators
export const loadUser = (email, id, name) => {
    return {type: GET_USER, data: {email:email, id:id, name:name}};
}

export const isLoaded = (loaded) => {
    return {type: IS_LOADED, loaded};
}

//DB에서 사용자 정보 읽어오는 함수
export const getUserFB = (email) => {
    console.log('액션 생성 : DB에서 유저 정보 읽어오기');
    return function (dispatch){
        user_db.doc(email).get().then((info) => {
            const id = info.get('id');
            const name = info.get('name');
            dispatch(loadUser(email, id, name));    //액션 발생시키기
            dispatch(isLoaded(true));
        })
    }
}

//Reducer
export default function reducer(state = initialState, action = {}){
    switch(action.type){
        //do reducer stuff
        case 'user/GET_USER': {
            return {email: action.data.email, id: action.data.id,
                name: action.data.name};
        }
        case 'user/LOADED': {
            return {...state, is_loaded: action.loaded};
        }
        default:
            return state;
    }
}

 

getUserFB가 Firestore에서 사용자 정보를 가져오는 부분이고

loadUser이 사용자 정보 상태를 업데이트 하는 액션을 생성하는 함수이다.

Reducer에서 상태 변경을 반영한다.

 

index.js에 Provider로 store를 주입한다.

ReactDOM.render(
  <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
  </Provider>,
  document.getElementById('root')
);

 

App.js에서 mapStateToProps와 mapDispatchToProps를 작성해서 상태를 만들고 Redux의 함수를 연결했다.

//스토어가 가진 상태값을 props로 받아오기 위한 함수
const mapStateToProps = (state) => {
  return {
    user_email: state.user.email,
    user_id: state.user.id,
    user_name: state.user.name,
    is_loaded: state.user.is_loaded,
  };
}

//상태 값을 변화시키기 위한 액션 생성 함수를 props로 받아오기 위한 함수
const mapDispatchToProps = (dispatch) => {
  return {
    load: (email) => {
      dispatch(getUserFB(email));
    },
  }
}

 

Profile.js에서 사이드바에 이름과 아이디가 출력되도록 useSelector을 이용했다.

import { useSelector, useDispatch } from 'react-redux';

function Profile(props) {
    const id = useSelector(state => state.user.id);
    const name = useSelector(state => state.user.name);

    return(
        <ProfileConatiner>
            <ProfileImg/>
            <h3>{name}</h3>
            <p>@{id}</p>
        </ProfileConatiner>
    )
};

 

 

직접 코드를 작성해보니 Redux에 대한 개념이 굉장히 헷갈렸지만

다행히도 몇시간 고민하다가 제대로 이해할 수 있었다.

+ Recent posts