<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>s_yeah.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 11 May 2022 19:23:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>s_yeah.log</title>
            <url>https://images.velog.io/images/s_yeah/profile/a7a2069c-f347-4bb3-80c8-c10e070a0548/KakaoTalk_Photo_2022-02-16-00-23-07.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. s_yeah.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/s_yeah" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[axios 요청 후 accessToken과 refreshToken이 undefined일 때]]></title>
            <link>https://velog.io/@s_yeah/accessToken%EA%B3%BC-refreshToken%EC%9D%B4-undefined%EC%9D%BC-%EB%95%8C</link>
            <guid>https://velog.io/@s_yeah/accessToken%EA%B3%BC-refreshToken%EC%9D%B4-undefined%EC%9D%BC-%EB%95%8C</guid>
            <pubDate>Wed, 11 May 2022 19:23:18 GMT</pubDate>
            <description><![CDATA[<p>서버와 요청을 주고받다보면 헤더설정이나 권한설정을 잘못할 경우 값을 제대로 불러오지 못하는 경우가 종종 있다. 
분명 로그인하고 나서 서버로부터 accessToken을 받아오고, refreshToken을 쿠키에 저장하도록 했는데도,
다시 토큰을 가지고 서버로 요청할때 서버에서 토큰 값을 undefined로 인식하고 있지 못한다면??</p>
<p>내가 어떻게 응답을 보내주고 있는지를 먼저 확인해봐야 한다. 어떤식으로 응답을 보냈고, 어떤 오류를 마주했었는지를 적어보려 한다. </p>
<h2 id="1-첫번째-시도">1) 첫번째 시도</h2>
<p>요청 코드 &gt;</p>
<pre><code class="language-javascript">const handleGetUserInfo = () =&gt; {
        console.log(accessToken);
        axios.get(`${REACT_APP_API_URL}/user`,
            {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;,
                    authorization: `Bearer ${accessToken}`,
                }
            }).then((result) =&gt; {
                console.log(result.data.data.userInfo);
            })

    }</code></pre>
<p>응답 결과 &gt; 요청헤더에 accessToken은 문제없이 들어가는걸 확인해볼 수 있는데, 어째서인지 cookie값이 인식되지 않는다.
<img src="https://velog.velcdn.com/images/s_yeah/post/7cd31006-d323-4aa7-b846-663a3f2c8b5f/image.png" alt="네트워크 결과1!">
<img src="https://velog.velcdn.com/images/s_yeah/post/023660dc-310c-40a2-9601-b1c24e80cae5/image.png" alt="refreshToken없음"></p>
<p>서버 반응 &gt; 둘다 undefined로 나온다.
<img src="https://velog.velcdn.com/images/s_yeah/post/1467ea22-76b1-47c1-84b2-719871342ea6/image.png" alt="서버콘솔"></p>
<p>사실 원래대로라면 콘솔에 <code>accessToken</code>이 찍혀있어야하는데(<code>refreshToken</code>만 없는거니까!) 여기서 <code>undefined</code>가 나온 이유는 <code>console.log(req.header.authorization);</code>로 콘솔을 확인 했기 때문이다....<code>console.log(req.headers.authorization);</code>로 확인하도록! 오타 주의~</p>
<p>바보갓은 나에모습,,
흑
<img src="https://velog.velcdn.com/images/s_yeah/post/fe24dde1-c1c0-4600-93d2-a2746ed1e6b8/image.png" alt="밈"></p>
<p>어쨌든 첫번째 시도는 refreshToken이 들어오지 않고 있다.</p>
<h2 id="2-두번째-시도">2) 두번째 시도</h2>
<p>요청 코드 &gt; refreshToken을 읽어오지 못한다는 것은 https 연결에서 withCredentials 조건이 제대로 설정되지 않았기 때문이다. 그래서 해당 조건을 만족하는 코드를 한줄 추가해줬다. 
<a href="https://github.com/axios/axios#request-config">axios 공식문서</a>를 확인해보니 get요청을 보낼 때 url다음으로 오게 되는 값은 config로 처리해준다. 해당 config에서도 Credential를 사용해 cross-site Access-Control 요청을 보내도록하면 cookie에 들어있는 refreshToken을 불러올 수 있게 되는 것이다.</p>
<pre><code class="language-javascript">// `withCredentials` indicates whether or not cross-site Access-Control requests
  // should be made using credentials
  withCredentials: false, // default</code></pre>
<pre><code class="language-javascript">const handleGetUserInfo = () =&gt; {
        console.log(accessToken);
        axios.get(`${REACT_APP_API_URL}/user`,
            {
                &quot;Content-Type&quot;: &quot;application/json&quot;,
                authorization: `Bearer ${accessToken}`,
          //refreshToken을 위해서 필요한 설정!!
                withCredentials: true

            }).then((result) =&gt; {
                console.log(result.data.data.userInfo);
            })

    }</code></pre>
<p>응답 결과 &gt; refreshToken이 쿠키에 잘 들어가 요청을 보내주고 있는 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/s_yeah/post/4c873415-9102-4fd5-ac7b-8e7115ef15a8/image.png" alt="refreshToken 있음">
하지만 어째서인지 accessToken은 인식하고 있지 못하는 상황 ㅠ
<img src="https://velog.velcdn.com/images/s_yeah/post/c84828ad-4b9f-4658-aa87-3a3667e80479/image.png" alt="accessToken이 없음"></p>
<p>서버 반응 &gt; 서버의 콘솔에서도 refreshToken은 있는데, accessToken은 undefined로 보여주고 있다. </p>
<p><img src="https://velog.velcdn.com/images/s_yeah/post/5502e27d-20ac-4755-81e9-4c6a4bca38c1/image.png" alt="서버콘솔"></p>
<br>

<h2 id="3-최종-요청-성공">3) 최종 요청 성공</h2>
<p>요청 코드 &gt; 
마찬가지로 <a href="https://github.com/axios/axios#request-config">axios요청 공식문서</a>에 기재되어 있는 걸 참고해보았다. 알고보니 헤더설정은 따로 커스텀한 헤더를 넣게될 경우 config안에서도 headers라는 키값으로 가지고 있어야 했다. </p>
<pre><code class="language-javascript">// `headers` are custom headers to be sent
headers: {&#39;X-Requested-With&#39;: &#39;XMLHttpRequest&#39;},</code></pre>
<p>헤더설정을 해주고, withCredentials는 따로 config안에 담은채로 요청을 보낸다.</p>
<pre><code class="language-javascript"> const handleGetUserInfo = () =&gt; {
        axios.get(`${REACT_APP_API_URL}/user`,
            {
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;,
                    Authorization: `Bearer ${accessToken}`,
                },
                withCredentials: true
            }

        ).then((result) =&gt; {
            console.log(result.data.data.userInfo);
        })

    }</code></pre>
<p>서버 반응 &gt; 
서버에서 토큰을 두개 전부 다 성공적으로 읽는 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/s_yeah/post/d5b903a7-cc4a-4a15-9035-2568d9c076d5/image.png" alt="2개의 토큰 전부 들어온 콘솔"></p>
<p>응답결과&gt; 
마찬가지로 authorization 헤더값에 accessToken이 들어가있고 cookie에도 refreshToken이 잘 담겨진 채로 요청을 보내고 있는 걸 확인할 수 있다.
<img src="https://velog.velcdn.com/images/s_yeah/post/b27f4122-637b-4b4a-aa04-3ebf1c136cb0/image.png" alt="네트워크에서도 쿠키와 헤더에 모두 토큰 존재"></p>
<p><img src="https://velog.velcdn.com/images/s_yeah/post/7cc31c41-03c0-48bf-827c-b8d6417b23be/image.png" alt="response 성공"></p>
<p>axios를 사용하는데 어째서인지 토큰값을 인지하지 못하고 있는 상황이라면 네트워크 탭에서 request headers와 서버에서 받아오는 값들을 자세히 비교해보면서,  헤더설정과 config설정을 주의깊게 확인하여 보내도록 하자!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[redux-persist와 persistor.purge()로 로그아웃 구현하기 (redux-toolkit )]]></title>
            <link>https://velog.io/@s_yeah/redux-persist%EC%99%80-persistor.purge%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-redux-toolkit</link>
            <guid>https://velog.io/@s_yeah/redux-persist%EC%99%80-persistor.purge%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-redux-toolkit</guid>
            <pubDate>Tue, 10 May 2022 19:38:45 GMT</pubDate>
            <description><![CDATA[<h2 id="1-새로고침-시-state-유지하기">1) 새로고침 시 state 유지하기</h2>
<p>redux-persist로 먼저 새로고침하더라도 redux의 상태들이 초기화되지 않도록 하는 과정을 먼저 거쳐야 한다. </p>
<p><strong>npm으로 설치하기</strong></p>
<pre><code class="language-javascript">npm install redux-persist</code></pre>
<p><strong>rootReducer.js</strong></p>
<pre><code class="language-javascript">import { combineReducers } from &quot;redux&quot;;
import loginSlice from &#39;./loginSlice&#39;;
import userInfoSlice from &#39;./userInfoSlice&#39;;
import modalSlice from &#39;./modalSlice&#39;;
import boardSlice from &#39;./boardSlice&#39;;

const rootReducer = combineReducers({
    login: loginSlice,
    userInfo: userInfoSlice,
    modal: modalSlice,
})

export default rootReducer;</code></pre>
<p><strong>store.js</strong></p>
<pre><code class="language-javascript">import { configureStore } from &#39;@reduxjs/toolkit&#39;
import {
    persistReducer,
    PERSIST,
    PURGE
} from &#39;redux-persist&#39;
import storage from &#39;redux-persist/lib/storage&#39;
import rootReducer from &#39;../reducers/rootReducer&#39;
import logger from &#39;redux-logger&#39;;

const persistConfig = {
    key: &#39;root&#39;,
    version: 1,
    storage,//localStorage에 저장해 store를 관리
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

const store = configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) =&gt;
  //미들웨어 작성시 에러 주의
        getDefaultMiddleware(
            {
                serializableCheck: {
                    ignoredActions: [PERSIST, PURGE],
                },
            }
        ).concat(logger)

})

export default store;</code></pre>
<blockquote>
<p><strong>주의</strong>: 발생할 수 있는 에러
<code>when using a middleware builder function, an array of middleware must be returned</code> 
<strong>=&gt;  에러가 발생했던 코드</strong></p>
</blockquote>
<pre><code class="language-javascript">middleware: (getDefaultMiddleware) =&gt; {
          /*에러나는 코드*/
          getDefaultMiddleware().concat(logger);
        getDefaultMiddleware(
            {
                serializableCheck: {
                    ignoredActions: [PERSIST, PURGE],
                },
            }
        )
}</code></pre>
<p>이처럼 애초에 middleware는 콜백함수 형태로 작성해야하는데 그게 아니라 함수를 실행?하는 것처럼 작성하여서 자꾸 에러를 뿜어내며 리액트를 렌더조차 못하고 있었던 것이었다. 따라서 한번만 <code>getDefaultMiddleware</code>를 써서 불러오도록 해야한다. </p>
<p><strong>index.js</strong></p>
<pre><code class="language-javascript">import App from &#39;./App&#39;;
import store from &#39;./store/store&#39;
import { persistStore } from &#39;redux-persist&#39;;
import { BrowserRouter } from &quot;react-router-dom&quot;;
import { Provider } from &#39;react-redux&#39;
import { PersistGate } from &#39;redux-persist/integration/react&#39;;


const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));

export let persistor = persistStore(store);
root.render(
  &lt;BrowserRouter&gt;
    &lt;Provider store={store}&gt;
      &lt;PersistGate loading={null} persistor={persistor}&gt;
        &lt;App /&gt;
      &lt;/PersistGate&gt;
    &lt;/Provider&gt;
  &lt;/BrowserRouter&gt;

);</code></pre>
<p>이렇게 설정해둘 경우 새로고침하면 더이상 store의 state들이 초기화되지 않는다. </p>
<br>

<h2 id="2-로그아웃시-유저-정보-삭제하기">2) 로그아웃시 유저 정보 삭제하기</h2>
<p>유저정보를 삭제하기 위해서는 store를 purge하는 작업을 거쳐야 한다. <code>persistConfig</code>에서 localStorage에 저장한 state들을 전부 초기화 시켜주기 위해서 필요한 과정이다. </p>
<p>공식문서에서는 다음과 같이 extraReducers를 사용해 적용하라고 한다. 
<a href="https://redux-toolkit.js.org/usage/usage-guide#use-with-redux-persist">리덕스 툴킷 사용 공식문서</a></p>
<pre><code class="language-javascript">import { PURGE } from &quot;redux-persist&quot;;

...
extraReducers: (builder) =&gt; {
    builder.addCase(PURGE, (state) =&gt; {
        customEntityAdapter.removeAll(state);
    });
}</code></pre>
<p>이를 적용해 logout이 실행되는 시점에서 만들어둔 extraReducer로 store에 접근 가능하도록 해야한다. </p>
<p><strong>loginSlice.js</strong></p>
<pre><code class="language-javascript">export const loginSlice = createSlice({
    name: actionName,
    initialState,
    reducers: {
        login: (state, action) =&gt; {
            state.isLogin = true;
            state.accessToken = action.payload;
        },
        logout: (state) =&gt; {
            state.isLogin = false;
        }
    },
  //초기화하고 싶은 state가 있는 slice마다 아래를 추가해야한다.
    extraReducers: (builder) =&gt; {
        builder.addCase(PURGE, () =&gt; initialState);
    }
})</code></pre>
<p>로그인 상태를 관리하는 loginSlice.js말고, userSlice.js같은 리듀서가 또 있다면 해당 리듀서에서도 추가하여 initialState로 초기화시킬 수 있도록 한다. extraReducers를 추가해주지 않으면 state를 초기화시키지 못하고 있는 것을 볼 수 있을 것이다. </p>
<p><strong>로그아웃이 실행되는 컴포넌트.js</strong></p>
<pre><code class="language-javascript">import { persistor } from &#39;../../index&#39;;

cons LogoutComponent = () =&gt; {
  //초기화 하는 함수
  const purge = async () =&gt; {
        await persistor.purge();
    }
}
return (
 &lt;NavButton onClick={async () =&gt; {
                            await handleLogout()
                            await setTimeout(() =&gt; purge(), 200)
                        }}&gt;Logout&lt;/NavButton&gt;

)</code></pre>
<p>onClick 이벤트가 발생했을 때 먼저 로그아웃 관리하는 함수를 전부 완료한 후에 purge()를 진행하도록 비동기처리했다. 
purge()함수를 만들어 처리하는 것에 대한 힌트는 <a href="https://github.com/rt2zz/redux-persist/issues/1015#issuecomment-612704310">redux-persist github 이슈:  purge() doesn&#39;t work</a>에 대한 답변중 하나를 참고했다. </p>
<p><strong><del>주의할점</del></strong>:로그아웃 함수(<code>handleLogout</code>)가 실행되기 전에 purge()해버리지 않도록 비동기처리와 <code>setTimeout()</code>처리를 함으로써 로그아웃 처리를 완료 후에 초기화하도록 해야한다. 초기화가 먼저 실행되버리면 로그아웃할 유저에 대한 정보가 먼저 사라져 버릴수도 있는 위험을 방지하기 위해서이다. </p>
<p>마지막으로 localStorage나 redux의 store가 잘 초기화되는지 확인만 해보면 된다.</p>
<h2 id="3-localstorage-초기화-확인">3) localStorage 초기화 확인</h2>
<p>: 크롬브라우저에서 개발자도구를 켜 application탭을 누르면 확인 가능하다.</p>
<p>로그인 시 localStorage의 상태
<img src="https://velog.velcdn.com/images/s_yeah/post/011cddd2-857c-4a7d-af7b-106fa45fc9b4/image.png" alt="로그인 시 localStorage"></p>
<p>로그아웃 시 localStorage의 상태
<img src="https://velog.velcdn.com/images/s_yeah/post/c146be2e-559a-49bf-b690-fce4ba5fae17/image.png" alt="로그아웃 시 localStorage"></p>
<h2 id="4-redux-store-초기화-확인">4) Redux store 초기화 확인</h2>
<p>이건 확인할 수 있는 방법이 여러가지가 있다. <code>getDefaultMiddleware().concat(logger)</code>로 찍히는 콘솔에서 직접 state를 확인해도 되고,</p>
<p>(action을 기준으로 상태값이 어떻게 달라지는지 확인 가능)
<img src="https://velog.velcdn.com/images/s_yeah/post/ba37bbea-2019-44c3-b9d7-be0d8635525b/image.png" alt="purge후 상태변화 콘솔"></p>
<p>마찬가지로 크롬 개발자도구에서 redux 확장프로그램을 설치해 redux 상태 변화를 확인해 볼수도 있다. </p>
<p><img src="https://velog.velcdn.com/images/s_yeah/post/c7478d16-e855-4b09-a570-711b8d6e965f/image.png" alt="purge후 초기화된 상태 in Redux탭"></p>
<p>두 방법으로 확인한 결과 모두 로그아웃시 상태값을 잘 초기화하는 것을 확인할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<img/> 이미지 경로 설정 - 리액트에서 적절한 상대경로 사용하기]]></title>
            <link>https://velog.io/@s_yeah/React%EC%97%90%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A1%9C%EB%94%A9-%EC%98%A4%EB%A5%98%EA%B0%80-%EB%82%A0%EB%95%8C-img%EC%9D%98-alt%EB%A7%8C-%ED%99%94%EB%A9%B4%EC%97%90-%EB%82%98%EC%98%AC%EB%95%8C</link>
            <guid>https://velog.io/@s_yeah/React%EC%97%90%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A1%9C%EB%94%A9-%EC%98%A4%EB%A5%98%EA%B0%80-%EB%82%A0%EB%95%8C-img%EC%9D%98-alt%EB%A7%8C-%ED%99%94%EB%A9%B4%EC%97%90-%EB%82%98%EC%98%AC%EB%95%8C</guid>
            <pubDate>Wed, 04 May 2022 20:18:39 GMT</pubDate>
            <description><![CDATA[<p><code>creact-react-app</code>으로 리액트 앱을 만들 때 이미지를 쉽게 다룰 수 있는 방법 중 하나가 <code>public/images</code> 폴더 안에 이미지들을 두어 관리하는 방법이다. 이 방법을 사용하는 이유는 <code>&#39;../../../assets/images/sth.png&#39;</code>와 같은 절대 경로를 쓰지 않고 <code>/images/sth.png</code>와 같은 상대 경로를 사용해 같은 소스의 이미지를 불러오도록 하기 위함이다. </p>
<p>이 상대경로를 제대로 설정해주지 않으면 나처럼 이미지 태그의 alt값만 떠있고 도통 이미지는 보이지 않는 슬픈 일이 일어날 수 있다. 
<img src="https://velog.velcdn.com/images/s_yeah/post/606083c3-4894-4aa2-9e60-c8427c74dca2/image.png" alt="alt값만 떠있는 이미지"></p>
<pre><code class="language-javascript">//파일 - src/assets/images.js
export const loadingImg = {
  //절대경로로 찾아오는 경우임
    loading: &quot;./images/loading.png&quot;
}</code></pre>
<p>이미지를 관리하고 꺼내쓰던 방법은 <code>src/assets/images.js</code>라는 파일에서 객체를 만들어 해당 객체에서 각 이미지 별로 키값을 부여해 public에서 이미지 src를 찾아오도록했다.</p>
<pre><code class="language-javascript">이미지를 불러오는 특정 Component.js
import { loadingImg } from &#39;../../assets/images&#39;;
...

return (
  &lt;img src={loadingImg.loading} alt={`loading`} /&gt;
)</code></pre>
<p>하지만!!!! 
위의 코드처럼 썼을때 처음 <code>https://domain.com</code> 홈에서 해당 이미지에 접근할 때는 문제가 없다가, 다른 페이지 (예시: <code>https://domain.com/post/id=sth</code>)에서 똑같은 이미지에 접근하려고 하면 이미지가 깨져서 로딩되는 것이었다. </p>
<p>그래서 개발자도구를 열어 문제가 생긴 이미지의 src를 어떻게 인식하고 있는지를 확인해봤다. </p>
<p><img src="https://velog.velcdn.com/images/s_yeah/post/78f0bc54-4110-4806-8684-6fd8a4899b8a/image.png" alt="이미지 소스 경로가 잘못된 경우"></p>
<p>불러오고 있는 src가 <code>https://localhost:3000/images/loading.png</code>가 되어야지 이미지를 제대로 불러올 수 있는데, 절대경로로 <code>&quot;./images/loading.png&quot;</code>불러왔기 때문에 도메인의 경로가 바뀌는 순간 이미지를 못찾아오는 듯해보였다. </p>
<h2 id="이미지-로딩-실험">이미지 로딩 실험</h2>
<pre><code class="language-javascript">// src/assets/images.js
export const loadingImg = {
  //절대경로로 찾아오는 경우임
    loading: &quot;./images/loading.png&quot;
}

//Component.js
import { loadingImg } from &#39;../../assets/images&#39;;
import loading from &#39;../../assets/loading.png&#39;;

//case1
&lt;img src={loadingImg.loading} alt={`loading`} /&gt;
//case2
&lt;img src=&#39;/images/loading.png&#39; alt={`loading`} /&gt;
//case3
&lt;img src={loading} alt={`loading2`} /&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/s_yeah/post/88751089-bc77-4fed-966b-004763dc762e/image.png" alt="이미지 경로 실험 결과"></p>
<p>case2, 3은 모두 정상적으로 이미지를 불러오는 것을 확인할 수 있었다. </p>
<p>개발자 도구를 열어 이미지의 자세한 src를 확인해보면 case2는 상대경로로 바로 이미지를 불러온 것이고, 
<img src="https://velog.velcdn.com/images/s_yeah/post/57c9e424-f6a6-4772-aae2-b68637d2aed1/image.png" alt="이미지 상대경로 개발자도구"></p>
<p>case3는 직접 png파일이 저장되었는 절대경로로 찾아들어간 것이다. 
<img src="https://velog.velcdn.com/images/s_yeah/post/9b2f1576-6f59-4023-af66-4838523f7318/image.png" alt="이미지 절대경로 개발자도구"></p>
<p>하지만 case3같은 경우 만약 다른 컴포넌트에서 해당 이미지를 접근하려 한다면 또다시 깨진 이미지를 마주하게 될 가능성이 있다. </p>
<p>case1은 처음부터 이미지가 깨져서 나온다. 아무리 images.js파일에서 이미지가 들어있는 절대경로로 src를 주어도 메인 주소가 바뀌면 해당 이미지를 불러오질 못하기 때문에 문제가 생긴다.</p>
<h2 id="해결방안">해결방안</h2>
<p>나의 경우에는 이미지를 관리하는 <code>src/assets/images.js</code>파일 속 경로를 전부 상대경로로 변경해주었다. </p>
<pre><code class="language-javascript">// src/assets/images.js
export const loadingImg = {
  //상대경로로 찾아오기
    loading: &quot;/images/loading.png&quot;
}</code></pre>
<p>위처럼 할 경우 어떤 컴포넌트에서든 <code>loadingImg</code>를 <code>import</code>해서 접근할때마다 문제없이 이미지를 불러올 수 있다.
위의 방법말고도 이미지를 불러올때는 <code>public/images</code> 경로에 이미지를 넣어두고 상대경로로 <code>import</code>해오는 것을 강력추천한다.</p>
<br>

<p>이와 같은 문제를 겪은 <a href="https://stackoverflow.com/questions/61690406/image-in-react-component-loading-on-one-page-but-not-the-other-one">스택오버플로우</a> 질문자에게 감사를 표하면서,,,</p>
<p><a href="https://www.w3schools.com/html/html_filepaths.asp">https://www.w3schools.com/html/html_filepaths.asp</a> 를 참고하여 더이상 같은 일들이 생기지 않게 파일 경로에 대해 알아두고 넘어간다...
상대경로를 반드시 사용하자!!</p>
<br>

<h2 id="추가-이미지를-상대경로로-import할-경우">추가) 이미지를 상대경로로 import할 경우</h2>
<p>이미지가 public/images에 있을 때 이미지를 아래와 같이 불러오고자 한다면,</p>
<pre><code class="language-javascript">//error
import logo from &quot;/images/logo.png&quot;;
      &lt;img src={logo} alt=&quot;Bepol Logo&quot; /&gt;
</code></pre>
<p>이러한 import 에러를 마주할 수 있다.</p>
<pre><code class="language-javascript">ERROR in ./src/components/Header.jsx 8:0-36
Module not found: Error: You attempted to import /images/logo.png which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.
You can either move it inside src/, or add a symlink to it from project&#39;s node_modules/.</code></pre>
<p>자세히 읽어보면, import는 같은 src 폴더 안에만 있는 것들을 상대경로로 불러올 수 있다고 말해주고 있다. </p>
<pre><code class="language-javascript">//good
//import 없애고 바로 src에서 상대경로로 연결
   &lt;img src=&quot;/images/logo.png&quot; alt=&quot;Bepol Logo&quot; /&gt;</code></pre>
<p>이처럼 public/images에 있는 이미지들은 상대경로로 불러오고자 한다면 import할 필요 없이 바로 src에 연결해주면 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[카카오API] 사용자의 현재위치를 동 단위로 변환하기]]></title>
            <link>https://velog.io/@s_yeah/%EC%B9%B4%EC%B9%B4%EC%98%A4API-%EC%82%AC%EC%9A%A9%EC%9E%90%EC%9D%98-%ED%98%84%EC%9E%AC%EC%9C%84%EC%B9%98%EB%A5%BC-%EB%8F%99-%EB%8B%A8%EC%9C%84%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@s_yeah/%EC%B9%B4%EC%B9%B4%EC%98%A4API-%EC%82%AC%EC%9A%A9%EC%9E%90%EC%9D%98-%ED%98%84%EC%9E%AC%EC%9C%84%EC%B9%98%EB%A5%BC-%EB%8F%99-%EB%8B%A8%EC%9C%84%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 03 May 2022 15:49:50 GMT</pubDate>
            <description><![CDATA[<p>동네를 기반으로 거래를 주고받는 프로젝트를 진행하다보니 필요한 기능 중에 하나가 사용자의 위치 정보이다. 위치정보를 받아서 동단위로 서비스를 이용하게끔 만들려면 사용자의 현재위치를 받아와 해당 위치를 동단위로 변환해 유저정보에 저장해두는 기능이 필요했다. </p>
<h2 id="geolocation-api-사용">Geolocation API 사용</h2>
<p>먼저 현재 위치를 받아오기 위해서는 Geolocation API를 사용해야 한다. 참고로 geoLocation API는 https에서만 사용가능하기 때문에 현재 개발 환경이 https인지 확인하고 사용해야 한다. 
<a href="https://developer.mozilla.org/ko/docs/Web/API/Geolocation_API/Using_the_Geolocation_API">Geolocation API 사용 공식문서</a></p>
<p><code>getCurrentPosition()</code> 함수를 이용해 현재 위치를 좌표로 가져올 수 있다. 함수의 첫번째 인자로는 콜백함수를 넣어 위치를 받아올 경우 콜백함수로 받아온 위치를 조작할 수 있다. 하지만 첫번째 콜백 호출이 실패할 경우 함수의 두번째 인자를 호출하기 때문에 오류 콜백을 두번째 인자로 둘 수 있다. </p>
<pre><code class="language-javascript">const doSomethingSuccess = (position) =&gt; {
  let latitude = position.coords.latitude;
  let longitude = position.coords.longitude;
}
const doSomethingError = (error) =&gt; {
  ...
}
navigator.geolocation.getCurrentPosition(doSomethingSuccess, doSomethingError);</code></pre>
<p>에러 메세지를 사용자에게 보여주고 싶다면 에러 코드에 따라 달리 표시 할 수도 있다. 
<a href="https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError">GeolocationPositionError 공식문서</a>
<code>GeolocationPositionError.code</code>를 사용해 에러 코드별로 메세지를 정해줄 수도 있다.</p>
<h2 id="카카오-지도-api-이용하기">카카오 지도 API 이용하기</h2>
<h3 id="카카오-api-사용을-위한-초기-설정">카카오 API 사용을 위한 초기 설정</h3>
<p><a href="https://developers.kakao.com/docs/latest/ko/getting-started/app">카카오api 사용 안내 문서</a>
먼저 카카오 디벨로퍼스에 가입하고 위의 링크에서 안내되어 있는대로 따라하면 된다. </p>
<p>위의 과정대로 하고 나면 api 요청에 필요한 rest API 키를 받을 수 있다. 
이를 외부로 노출하지 않고 사용하기 위해서는 클라이언트에 <code>.env</code>파일을 만들어 준다.
<img src="https://velog.velcdn.com/images/s_yeah/post/66c2f77a-add2-4b17-abb4-070fa7a8c2ec/image.png" alt="env파일 경로"></p>
<p><code>.env</code>파일을 만들어 <code>REACT_APP_KAKAO_API_KEY=키값</code> 이런식으로 작성해주고 해당 값이 필요할 때는 <code>process.env.REACT_APP_KAKAO_API_KEY</code>로 값을 불러와 사용하면 된다. <code>.gitignore</code>파일에 <code>.env</code>라고 적음으로써 깃에 실수로 <code>.env</code>파일을 올리는 걸 방지한다. 보다 자세한 단계별 설명은 <a href="https://www.geeksforgeeks.org/how-to-hide-your-api-keys-from-public-in-reactjs/">API key 보안 유지하는 방법</a> 링크를 참고해보면서 설정하면된다. </p>
<blockquote>
<p><strong>주의</strong> : api키의 변수명은 반드시 <code>REACT_APP_</code>으로 시작해야한다.(상세 에러 상황은 아래의 <a href="#%EC%97%90%EB%9F%AC-%EB%B0%9C%EC%83%9D-%EB%B0%8F-%ED%95%B4%EA%B2%B0">에러해결 참고</a>)</p>
</blockquote>
<br>

<h3 id="좌표를-동-단위로-변환">좌표를 동 단위로 변환</h3>
<p>이제 geolocation으로 받아온 x,y좌표를 kakao api로 요청을 보내 행정구역 정보로 받아오고 그 중 동 단위의 정보만 저장할 것이다. 
카카오 지도 api 공식문서: <a href="https://developers.kakao.com/docs/latest/ko/local/dev-guide#coord-to-district">좌표로 행정구역 정보 받아오기</a></p>
<p>요청해주는 함수는 공식문서에서 요구하는대로 headers에 rest api 키를 넣어서 보내준다. </p>
<pre><code class="language-javascript">/* 카카오지도 API로 현재 유저 좌표를 동단위로 변환 */
    const alterAddress = (position) =&gt; {
        let x = position.coords.longitude;
        let y = position.coords.latitude;
        if (x &amp;&amp; y) {
            axios.get(
                `https://dapi.kakao.com/v2/local/geo/coord2regioncode.json?x=${x}&amp;y=${y}`,
                { headers: { Authorization: `KakaoAK ${process.env.KAKAO_REST_API_KEY}` } }
            ).then((result) =&gt; {
              //법정동 기준으로 동단위의 값을 가져온다
                let location = result.documents[0].region_3depth_name;

            })
        }
    }</code></pre>
<h2 id="에러-발생-및-해결">에러 발생 및 해결</h2>
<p>위처럼 요청을 보내자 에러가 생겼다.</p>
<img src="https://media.giphy.com/media/shqIUlRQmrtVKPBZdS/giphy.gif" width="480" height="270"/>

<p>침착하게 어떤 에러가 났는지를 살펴 보자.</p>
<p>해당 함수를 실행시키고 어떤 값을 받아오는지 개발자도구 네트워크 탭을 확인해보았다. response payload에는 </p>
<pre><code class="language-javascript">{&quot;errorType&quot;:&quot;AccessDeniedError&quot;,&quot;message&quot;:&quot;wrong appKey(undefined) format&quot;}</code></pre>
<p>위와같이 잘못된 api key때문에 접근이 거절되었다는 에러메세지를 보내주고 있었다. </p>
<p>이에 따라 request요청을 살펴보니 headers에 넣어둔 api 키값을 가져오지 못하고 있었다</p>
<pre><code class="language-javascript">{ headers: { Authorization: `KakaoAK ${process.env.KAKAO_REST_API_KEY}` </code></pre>
<p>요청 헤더 속 <code>Authorization</code>이 <code>undefined</code>로 제대로 들어가지 못하고 있었다. 
<img src="https://velog.velcdn.com/images/s_yeah/post/d646a86a-834f-40e4-a191-d3b430e3781c/image.png" alt="request headers"></p>
<p><code>process.env.KAKAO_REST_API_KEY</code>를 인식하지 못하고 있었다. 알고보니 React 앱에서는 <code>.env</code> 파일에서 변수값을 사용하려면 <code>REACT_APP_</code>로 시작하는 변수명을 사용해야 한다. 저렇게 만든 이유는 같은 이름일 경우 실수로 키값이 노출될 수도 있기 때문에 일부러 지정해두었다고 한다.(<a href="https://create-react-app.dev/docs/adding-custom-environment-variables/">env파일사용 공식문서</a>)
<code>.env</code>파일을 수정하고 나면 돌아가고 있던 react앱을 ctrl+c 로 종료시킨 후 다시 <code>npm start</code>해줘야 한다. </p>
<p>다시 앱을 실행해주면,,,
<img src="https://velog.velcdn.com/images/s_yeah/post/806a4f38-fca4-4489-ae9b-314bbddb7104/image.png" alt=""></p>
<p><strong><code>response.payload</code>에 제대로 정보를 받아오는 것을 볼 수 있다!!!</strong></p>
<p>이제 이것을 개발자가 원하는 대로 데이터를 뽑아 사용하면 된다. </p>
<br>
항상 api 요청 보낼때는 headers가 제대로 들어가고 있는지, 소대문자 체크, 값체크 등등 주의를 기울여서 해줘야한다는 걸 다시한번 깨달았다,,,~~ㅎ]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] git pull error: 브랜치 merge할 때 발생하는 에러]]></title>
            <link>https://velog.io/@s_yeah/Git-git-pull-error-%EB%B8%8C%EB%9E%9C%EC%B9%98-merge%ED%95%A0-%EB%95%8C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@s_yeah/Git-git-pull-error-%EB%B8%8C%EB%9E%9C%EC%B9%98-merge%ED%95%A0-%EB%95%8C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Fri, 29 Apr 2022 07:36:25 GMT</pubDate>
            <description><![CDATA[<p>만약 나의 origin repository가 upstream과 동일하게 최신화 되어있는 상태에서, 내 로컬은 아직 이전 버전인 상태인데 파일을 push 하고자 한다면? 깃은 아마도 pull을 먼저 진행하고 push하라고 에러를 뿜어줄 것이다. </p>
<p>안전하게 merge하기 위해서는 여러 에러들을 거쳐가야 한다.
매번 같은 에러를 만날 때마다 머리를 싸맸는데 그중에 한가지 방법을 기록해두고자 한다. </p>
<h3 id="1-로컬과-원격-repo-버전-맞추기">1) 로컬과 원격 repo 버전 맞추기</h3>
<p>현재 내 로컬에서 아직 git add나 git commit되지 않은 변경사항이 있다면, <code>git pull upstream branch</code> 나 <code>git pull origin branch</code> 둘 다 되지 않을 것이다. 이럴 땐 working tree에 남아있는 수정사항들이 있는지 먼저 확인해보고 있다면 전부 add 처리를 해줘야 한다. 그런 다음에 git pull을 시도했을 때, 순탄히 되지 않을 가능성이 높다.  </p>
<p>따라서 이런 에러를 마주하고 싶지 않다면 반드시 <code>로컬 저장 후 커밋 -&gt; push -&gt; 내 원격 레포 -&gt; 내 원격 레포와 업스트림 레포를 fetch &amp; merge로 최신화하기</code> 이러한 순서로 진행하길 바란다. </p>
<h3 id="2-git-push가-되지-않을-때">2) git push가 되지 않을 때</h3>
<p>나의 repository가 이미 업스트림에 맞게 최신화 되어있기 때문에 브랜치가 한단계 더 앞에 있어 현재 내 로컬브랜치와 출발점이 맞지 않아 push가 되지 않는 상황이 생긴다. </p>
<p>이럴 경우 뜨는 에러는 </p>
<pre><code class="language-ERROR">warning: Pulling without specifying how to reconcile divergent branches is
discouraged. You can squelch this message by running one of the following
commands sometime before your next pull:

  git config pull.rebase false  # merge (the default strategy)
  git config pull.rebase true   # rebase
  git config pull.ff only       # fast-forward only

You can replace &quot;git config&quot; with &quot;git config --global&quot; to set a default
preference for all repositories. You can also pass --rebase, --no-rebase,
or --ff-only on the command line to override the configured default per
invocation.</code></pre>
<p>이러하다. 친절하게 깃에서 알려주는 대로 <code>git config pull.rebase true</code>이런 명령문으로 에러를 해결해 볼수도 있겠지만, config 설정을 해두면 원치 않을 경우에도 위의 설정이 적용되어 나중에 불편한 상황이 올 수도 있다. </p>
<p>그렇다면 지금 할 수 있는 선택은 내 로컬에서 새로운 브랜치를 만들고,
그곳에서 rebase를 해준다음, 
원래 머지하려했던 나의 로컬 브랜치로 merge하는 법이다. 
이렇게 진행하면 새로운 브랜치에 최신화된 코드를 내 로컬로 가져와서 나의 다른 로컬 브랜치랑 합치는 작업을 해볼 수 있다.
다른 브랜치를 새로 만든 이유는 혹시 모를 손실을 막기 위해 따로 브랜치를 파서 진행한다. </p>
<h4 id="1-새로운-브랜치-만들기">1) 새로운 브랜치 만들기</h4>
<p><code>git checkout -b 새로운브랜치 합치려는브랜치</code>: 새로운 브랜치를 만들고 해당 브랜치로 변경해준다.</p>
<h4 id="2-업스트림의-원하는-브랜치를-pull받아오기rebase">2) 업스트림의 원하는 브랜치를 pull받아오기(rebase)</h4>
<p><code>git pull --rebase upstream 합치려는브랜치</code>
: rebase처리해줌으로써 깃 브랜치 상태를 최신화 시켜줄 수 있다.</p>
<h4 id="3-rebase-과정-중-발생할-수-있는-에러">3) rebase 과정 중 발생할 수 있는 에러</h4>
<p>에러가 없었으면 좋겠지만 대부분 발생할 가능성이 크다. upstream에서 누군가가 내가 작업한 파일에서 변경을 했다면, 자동으로 merge될수 없기 때문이다. 이럴 때는 vscode에서 코드를 보고 살릴 부분을 확인하고 저장 후 
<code>git add .</code> 한 후에 <code>git rebase --continue</code>를 해준다. 
위와 같은 과정이 한번에 해결되는 건 아니고 충돌이 나는 부분이 있을 때마다 발생한다. 
따라서 발생할 때마다 수정사항 저장  <code>git add .</code>(vscode source control에서 staged changes로 올라갔는지 한번 확인) 하고, <code>git rebase --continue</code>를 진행해준다. 
계속 진행하다 보면 rebase가 완료되었다는 메세지를 볼 수 있게 된다! 따라서 완료 메세지를 보기 전까지는 계속해서 위의 과정을 진행해줘야 한다. </p>
<h4 id="4-rebase-완료-후-합치고자-하던-브랜치와-merge">4) rebase 완료 후 합치고자 하던 브랜치와 merge</h4>
<p>rebase가 되었다면 이제 내가 원하는 브랜치와 merge하는 작업만 남았다. 
    <code>git checkout 합치려던브랜치</code>로 브랜치를 바꿔주고,
    <code>git merge --no-ff 새로만들었던브랜치</code>를 하고 
    <code>git push origin dev</code>를 해준다. </p>
<p> 이제 무사히 나의 로컬에 있던 파일들이 업스트림 파일들과 잘 머지 되어 합쳐진 최신 버전으로 내 원격 레포지토리에 올라가는 것을 확인할 수 있다. 그리고 나서 upstream 브랜치도 최신화시켜야 한다면, pull request를 열어 머지 작업을 해주면 된다. </p>
<p><br><br>
항상 같은 문제가 발생하고 같은 오류 메세지를 만나다가 이번에는 제대로 해결해보자 싶어 해결 후 기록해두었다. 물론 rebase하는 것 말고 다른 좋은 방법들이 있을테니 좀 더 깃 사용법에 대해 학습해봐야겠다. 우선은 rebase로 머지를 해주고 최신화를 시켜주는 방식을 하나 익혔다. 
무엇보다도 <strong>내 원격 레포를 fetch upstream으로 아무 생각없이 하지 말고!!!!! *<em>먼저 *</em>내 로컬의 변경사항들을 잘 저장하고 커밋해두고 push까지 한 후에!!</strong> 진행하는 것이 베스트인 것 같다. 깃으로 다시 한번 소중한 교훈을 얻어간다....ㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error] Error: ENOENT: no such file or directory, uv_cwd 에러]]></title>
            <link>https://velog.io/@s_yeah/Error-Error-ENOENT-no-such-file-or-directory-uvcwd-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@s_yeah/Error-Error-ENOENT-no-such-file-or-directory-uvcwd-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Mon, 25 Apr 2022 14:02:18 GMT</pubDate>
            <description><![CDATA[<p>컴퓨터에 무언가 문제가 생겼을 때 기술자에게 질문을 던진다면 전원을 켰는지, 켰다면 껐다 켜보라는 근본적인 해결책 같이 안느껴지는 해결책을 되돌려줄 때가 있다.  </p>
<p>이번 에러는 바로 그렇고 그런 근본 에러가 아니었나 싶다. </p>
<p>프로젝트를 상쾌한 마음으로 시작하려 하는데    <code>npm install</code>이나 <code>npm start</code>부터 안된다면? 그렇다면 터미널 창을 껐다 켜보는 것을 추천한다. </p>
<br>
특히나 'Error: ENOENT: no such file or directory, uv_cwd'라는 에러메세지를 만났을 때는 더더욱 말이다. 

<p>아래의 링크를 보면 나와 같은 에러를 발견한 github issue가 있다. 
<a href="https://github.com/AnandChowdhary/run-url/issues/1">https://github.com/AnandChowdhary/run-url/issues/1</a></p>
<p>그중에 제일 도움이 됐던 답변은 
바로 아래 링크의 답변이다.</p>
<p><a href="https://github.com/AnandChowdhary/run-url/issues/1#issuecomment-759356645">첫번째 해결책 링크</a></p>
<blockquote>
<p>&quot; 그냥 터미널 창을 껐다 켜보세요. &quot; </p>
</blockquote>
<p>덕분에 아무 문제 없이 문제가 해결됐다!!!
<br></p>
<p>하지만 혹시나 껐다 켜도 안되는 상황이 있을 경우를 위해 두번째 해결책인 아래의 링크를 첨부한다. 
<a href="https://github.com/foreversd/forever/issues/200#issuecomment-57741881">두번째 해결책 링크</a>
하지만 다수의 의견을 보면 두번째 해결책처럼 <code>sudo npm cache clean -f</code>을 해도 되겠지만 터미널을 재시작하는것으로 충분하다면 할필요는 없다. 그러니 반드시 먼저 껐다 켜보고 시도해보도록...</p>
<p>여러 이유가 있겠지만 브랜치 변경할 때 생길수도 있고, 처음 만들때 생성해둔 폴더를 중간에 지웠는데 알고보니 모듈들이 폴더를 지우고 새로 만든 폴더 경로를 찾지 못해오는거일수도 있다.그러니 반드시 새로고침을 해보도록 하자!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[깃헙 소셜 로그인 시 요청 오류를 만났을때 (부제: how I met your headers)]]></title>
            <link>https://velog.io/@s_yeah/%EA%B9%83%ED%97%99-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C-%EC%9A%94%EC%B2%AD-%EC%98%A4%EB%A5%98%EB%A5%BC-%EB%A7%8C%EB%82%AC%EC%9D%84%EB%95%8C-%EB%B6%80%EC%A0%9C-how-I-met-your-headers</link>
            <guid>https://velog.io/@s_yeah/%EA%B9%83%ED%97%99-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C-%EC%9A%94%EC%B2%AD-%EC%98%A4%EB%A5%98%EB%A5%BC-%EB%A7%8C%EB%82%AC%EC%9D%84%EB%95%8C-%EB%B6%80%EC%A0%9C-how-I-met-your-headers</guid>
            <pubDate>Mon, 25 Apr 2022 13:13:30 GMT</pubDate>
            <description><![CDATA[<center>도무지 교류할 생각이 없어보이는 클라이언트(왼쪽)와 서버(오른쪽)</center>

<p><br><br>
프로젝트를 할때, 테스트 케이스도 전부 다 통과를 하는데?!?!?
막상 <code>npm run start</code>를 해보면 서버와 클라이언트가 뭔 원수를 졌는지 <code>cannot GET/</code> 이런것만 띄워두거나 자꾸 콘솔창에서 <code>undefined</code> 만을 돌려주고 있는 야속한 상황이 생길 때가 있다.</p>
<p>이번 스프린트에서는 클라이언트에서 서버로 요청을 보낸 후 서버에서 github OAuth인증을 받아 accessToken을 github에서 받은 후 다시 클라이언트로 보내주는 과정을 구현했다. </p>
<p>여러가지 난관이 있었지만 궁극적으로 블로그에 기록할만큼의 힘듦을 선사한 오류를 기록해두고자 한다. </p>
<p>우선 간략하게 소셜로그인에 사용되는 OAuth인증을 살펴보면 다음과 같다.</p>
<p>
<img src="https://images.velog.io/images/s_yeah/post/090ae08e-69c0-4a38-8603-c852aec82644/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-03-19%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.15.39.png"/></p>


<p>이중에서도 4)단계와 5)단계를 진행하는 과정을 조금 더 자세히 보면, </p>
<pre><code class="language-js">axios.post(&#39;https://github.com/login/oauth/access_token&#39;, dataGroup,
     {
       //반드시!!!!!json 타입으로 받아오겠다고 적어둬야함, 안그러면 키 값을 읽어낼수가 없음...
       headers: { &#39;Accept&#39;: &#39;application/json&#39; }
     })
     .then((result) =&gt; {
       console.log(result.data)
       res.status(202).send({ accessToken: result.data.access_token })
     })</code></pre>
<p>Headers 안넣어서 json으로 변경이 안되었을 때, 요청 성공 시 응답 받은 값(<code>res.data</code>)을 확인해보면 , </p>
<p align="center">
<img alt="accessToken문자열로 출력" src="https://images.velog.io/images/s_yeah/post/55045bbb-fa7d-4e3e-9350-1f0a1d95714c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-03-18%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.11.53.png"/></p>

<p>이렇게 나오는 이유는!</p>
<p>문자열로 인식되어서 키값으로 access_token을 가져오지 못함
data에 빈객체만이 돌려주고 있을 뿐...</p>
<p align="center">
<img alt="res.data응답값 빈객체" src="https://images.velog.io/images/s_yeah/post/2b291bfb-b947-4a10-aa0e-976653288fea/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-03-18%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.13.15.png"/></p>


<p>그래서 header 설정을 반드시 해줘서 보내줘야 한다. </p>
<pre><code class="language-javascript">{
   //반드시!!!!!json 타입으로 받아오겠다고 적어둬야함, 
   //안그러면 키 값을 읽어낼수가 없음...
       headers: { &#39;Accept&#39;: &#39;application/json&#39; }
     }</code></pre>
<p>주석에도 반드시!!라고 적어둔것처럼 정말 중요해서 다시한번 코드로 남겨둔다. 이걸 안 적어두니까 json타입으로 받아오질 못해서 객체의 키값(예시: <code>result.data.data.accessToken</code>)으로 <code>accessToken</code>을 받아올 수 없는 것이기 때문이다.</p>
<p>원하는 데이터 값을 받아오고 싶다면 api문서를 잘 읽고 요구사항이 뭔지를 제대로 본 후에 요청을 보내도록 하자. 
물론 깃헙 accessToken을 받아오는 과정을 설명하는 문서(<a href="https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#2-users-are-redirected-back-to-your-site-by-github)%EC%97%90%EC%84%9C%EB%8A%94">https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#2-users-are-redirected-back-to-your-site-by-github)에서는</a> headers에 대한 언급이 없다. parameters를 설명을 봐도 body에 담아줘야할 필수 요소들만 써져 있을 뿐이다..
<br>
혹여나 github 로그인을 소셜로그인으로 달았다가 accessToken이 없는 경우에 마주했다면 반드시 headers를 제대로 보내주고 있는지를 확인해볼 필요가 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 관계형 데이터베이스, SQL 기본 구문]]></title>
            <link>https://velog.io/@s_yeah/SQL-%EA%B4%80%EA%B3%84%ED%98%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-SQL-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EB%AC%B8</link>
            <guid>https://velog.io/@s_yeah/SQL-%EA%B4%80%EA%B3%84%ED%98%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-SQL-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EB%AC%B8</guid>
            <pubDate>Tue, 29 Mar 2022 06:06:19 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터베이스의-필요성">데이터베이스의 필요성</h1>
<p>데이터를 저장하고 사용하는데는 다양한 방법이 존재한다. In-Memory방식으로 javascript에서 프로그램이 실행될때만 저장되는 방식과, 엑셀 시트나 csv파일 형태로 저장하는 방식이 있다. In-Memory 방식의 경우 프로그램이 종료되면 사용했던 데이터도 모두 사라지기 때문에 예기치 못한 상황에서 데이터를 보호하기가 어렵다. 파일을 읽는 방식으로 작동하는 경우도 데이터가 필요할 때마다 전체 파일을 읽어와야하므로 크기가 크면 작업이 버거워지는 한계가 있다. </p>
<p>하지만 관계형 데이터베이스를 사용할 경우 데이터가 프로그램의 수명에 의존하지 않아도 될 뿐더러, 하나의 파일에 해당하는 데이터를 한 개의 테이블로 저장하고 여러개의 테이블을 가질 수 있다. </p>
<p>결국 인메모리 방식이나 데이터 파일을 불러와 사용하는 방식 모두 한계가 있어 이를 보완하고자 데이터 베이스를 사용한다.데이터 베이스를 사용하는 이유를 축약하면 다음과 같다.</p>
<blockquote>
<p>(&#39;데이터베이스를 지탱하는 기술&#39; 참고)</p>
</blockquote>
<ul>
<li>필요한 데이터를 빠르게 반환 가능</li>
<li>대용량 데이터는 메모리 외에서 관리 필요
: 메모리의 크기 제한, 데이터 손실의 경우 등의 한계 극복</li>
<li>무결성 관리를 위해 필요</li>
<li>병렬성 제어
: 배타 제어(한사람이 데이터를 수정할때 다른 사람은 수정하지 못하도록 하는 제어)로 일관성을 지킴과 동시에 실용성을 높이기 위해 병렬적으로 제어 가능하도록 한다.</li>
</ul>
<h1 id="sqlstructured-query-language">SQL(Structured Query Language)</h1>
<p>관계형 데이터베이스에서 사용하는 데이터베이스 용 프로그래밍 언어로 MySQL, Oracl, SQLite 등에서 SQL 구문을 사용할 수 있다. 관계형 데이터베이스는 데이터가 구조화된 테이블로 이루어져 있다. NoSQL은 반대로 테이블을 사용하지 않는 문서 지향 데이터베이스다. SQL은 데이터가 구조화된 데이터베이스에서만 사용가능한 언어이다.</p>
<p><em>* Query? 쿼리는 &#39;질의문&#39;이라는 뜻으로 저장되어 있는 데이터를 필터하기 위한 질의문이다. 검색할 때 입력하는 검색어로 데이터를 필터링해주는데 이때 작성하게 되는 검색어가 일종의 쿼리라고 볼 수 있다.</em></p>
<h1 id="sql-주요-문법">SQL 주요 문법</h1>
<p>기본적으로 데이터의 조회, 삽입, 갱신, 삭제 등을 다룰 수 있어야 한다. </p>
<p>SQL의 기본 문법을 작성하는데 필요한 튜토리얼은 <a href="https://www.w3schools.com/quiztest/quiztest.asp?qtest=SQL">https://www.w3schools.com/quiztest/quiztest.asp?qtest=SQL</a> 여기서 점검해보기 좋다. </p>
<p>모든 sql 문법을 다 적기보다는 자주 쓰이거나 쓰다가 헷갈렸던 구문들을 몇개 정리해보려고 한다. </p>
<h2 id="insert-into">INSERT INTO</h2>
<p> 새로운 레코드를 테이블에 추가한다. </p>
<p> _ 컬럼 추가와 다른점 _
 : 해당 칼럼에 <code>ALTER TABLE 테이블명 ADD 컬럼명</code> 쿼리문</p>
<h2 id="select-where">SELECT WHERE</h2>
<h2 id="like">LIKE</h2>
<h2 id="functions">Functions</h2>
<ul>
<li>max(컬럼명)</li>
<li>min(컬럼명)</li>
<li>avg(컬럼명)</li>
<li>count(컬럼명)</li>
<li>sum(컬럼명)<h2 id="group-by">GROUP BY</h2>
그룹별 정보를 조회해보고 싶을 때 사용한다. 반드시 집계함수, 그룹함수와 함께 사용해야 한다. <h2 id="order-by">ORDER BY</h2>
</li>
</ul>
<h2 id="alias">ALIAS</h2>
<h2 id="in">IN</h2>
<h2 id="drop-table--truncate-table">DROP TABLE / TRUNCATE TABLE</h2>
<p>둘 다 테이블을 제거하는 쿼리문이다. 차이가 있다면 <code>DROP TABLE</code>은 테이블 자체를 삭제하지만, <code>TRUNCATE TABLE</code>은 테이블의 구조는 남아있으나 테이블 안의 데이터를 전부 삭제한다는 차이가 있다. </p>
<h1 id="스키마-디자인">스키마 디자인</h1>
<p>앱에 필요한 테이블과 필드, 그리고 관계를 부여할 수 있다.
1:N, N:N 관계를 이해하고, 데이터베이스에서 테이블을 조작할 수 있다.
Foreign Key, Primary Key에 대해 이해할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조/알고리즘] 시간 복잡도]]></title>
            <link>https://velog.io/@s_yeah/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84</link>
            <guid>https://velog.io/@s_yeah/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84</guid>
            <pubDate>Wed, 02 Mar 2022 08:08:24 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/s_yeah/post/daed92af-2b81-4d94-97a6-105844c538ba/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-03-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.52.37.png" alt="시간복잡도 그래프"></p>
<h2 id="시간복잡도">시간복잡도</h2>
<p>알고리즘 문제를 풀 때 효율적인 방법을 고민하기 위해 고려해야할 조건 중 하나는 시간 복잡도이다. 시간 복잡도를 고려한다는 것은 <strong>입력값의 변화에 따라 연산을 실행할 때, 연산 횟수에 비해 시간이 얼만큼 걸리는지를 확인한다는 것</strong>이다. 즉, 입력값이 커짐에 따라 증가하는 시간의 비율이 최소화될 수록 효율적인 알고리즘을 구현했다고 할 수 있다. </p>
<p>대부분 Big-O 표기법으로 시간 복잡도를 나타낸다. Big-O 표기법은 최악의 경우를 나타내므로, 만약 연산에 최대 1시간이 걸릴경우, Big-O 표기법으로 해당 연산은 1시간짜리 시간복잡도를 가진 연산이라고 본다. </p>
<h3 id="o1">O(1)</h3>
<p>: 입력값의 크기와 상관 없이 즉시 출력값을 알 수 있는 시간 복잡도</p>
<pre><code class="language-js">function algorithm(arr,idx) {
  return arr[idx];
}
// arr의 크기가 아무리 커져도 출력값을 바로 얻어낼 수 있다.
// arr의 길이가 1일때나 1000000일때나 리턴값을 얻는데는 똑같은 시간이 소요된다.</code></pre>
<h3 id="on">O(n)</h3>
<p>: 입력값이 증가함에 따라 시간도 같은 비율로 증가하는 시간 복잡도 - linear complexity
입력값의 크기가 1일때 어떤 연산에 1초의 시간이 걸리고 크기가 100일 때 100초의 시간이 걸렸다면 O(n) 시간 복잡도를 가진다고 본다.</p>
<pre><code class="language-js">//입력값이 1 증가할 때, 실행시간은 1초씩 증가
//입력값 크기:1 -&gt; 1초
//입력값 크기:2 -&gt; 2초
//입력값 크기:100 -&gt; 100초
function O_N_algorithm(n){
  for(let i=0;i&lt;n;i++){
    //연산 시 1초의 시간이 걸림
  }
}
//입력값이 1 증가할 때, 실행시간은 2초씩 증가
//입력값 크기:1 -&gt; 1초
//입력값 크기:2 -&gt; 4초
//입력값 크기:100 -&gt; 200초
function O_2N_algorithm(n){
  for(let i=0;i&lt;2n;i++){
    //연산 시 1초의 시간이 걸림
  }
}</code></pre>
<p>두번째 코드 예시의 경우 O(2n)으로 생각할 수도 있지만, 입력값의 크기가 커질 수록 계수의 의미는 크게 영향을 미치지 않기 때문에, 같은 비율로 증가한다면 모두 O(n) 시간 복잡도를 가진다고 본다. </p>
<h3 id="ologn">O(logn)</h3>
<p>: 입력값의 크기가 커질수록 logn에 비례하여 증가하는 시간 복잡도 - logarithmic complexity
이진 검색과 같이 입력값의 크기에서 특정 값을 찾을 때까지 절반씩 줄여나가는 연산을 가질 경우 O(logn)의 시간 복잡도를 가진다.</p>
<pre><code class="language-js">//N(입력값의 크기)을 2번 나누기 k번 실행으로 1이 될때까지 연산
//2^k = N  -&gt;  log2N 
function binarySearch(n, arr, start, end){
  if(start&gt;end){
    return -1;
  }
  let idx = parseInt((start+end)/2);
  if(arr[idx] === n) {
    return idx;
  }else if(arr[idx] &gt; n) {
    //찾고자 하는 숫자가 배열의 반절의 값보다 작을 때
    return binarySearch(n, arr, start, idx-1);
  }else{
    //찾고자 하는 숫자가 배열의 반절의 값보다 클 때
    return binarySearch(n, arr, idx+1, end);
  }
}</code></pre>
<p>또 다른 예제로는, </p>
<pre><code class="language-js">for(let i=0;i&lt;n;i++){
  i *= k;
}
//k배수만큼 i가 증가하며 n으로 도달
//k^i === n  -&gt; log(k) n </code></pre>
<h3 id="on2">O(n^2)</h3>
<p>: 입력값의 크기가 커질수록 시간이 n의 제곱수의 비율로 증가하는 시간 복잡도 - quadratic complexity
입력값의 크기가 1일때 어떤 연산에 1초의 시간이 걸리고 크기가 5일 때 25초의 시간이 걸렸다면 O(n^2) 시간 복잡도를 가진다고 본다.</p>
<pre><code class="language-js">//입력값이 1 증가할 때, 실행시간은 1초씩 증가
//입력값 크기:1 -&gt; 1*1 = 1초
//입력값 크기:2 -&gt; 2*2 = 4초
//입력값 크기:100 -&gt; 100*100 = 100초
function O_quadratic_algorithm(n) {
    for (let i = 0; i &lt; n; i++) {
        for (let j = 0; j &lt; n; j++) {
        // do something for 1 second
        }
    }
} 
//n^3씩 실행시간 증가
function another_O_quadratic_algorithm(n) {
    for (let i = 0; i &lt; n; i++) {
        for (let j = 0; j &lt; n; j++) {
            for (let k = 0; k &lt; n; k++) {
            // do something for 1 second
            }
        }
    }
}</code></pre>
<p>O(2n),O(3n)이 O(n)과 같은 시간 복잡도로 표기되는 것과 마찬가지로 n^2과 n^3, n^4, n^5 모두 O(n^2)로 표시한다.</p>
<h3 id="o2n">O(2^n)</h3>
<p>: 입력값의 크기가 증가함에 따라 매번 2배씩 시간이 늘어나는 시간 복잡도 - exponential complexity</p>
<pre><code class="language-js">//재귀로 구현하는 피보나치 수열 
//n이 100이상 넘어갈 경우 평생 결과값을 못받을 수도 있다.
function fibonacci(n) {
    if (n &lt;= 1) {
        return 1;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}</code></pre>
<p><strong>시간복잡도의 빠른 순서대로 비교를 하자면</strong>
: O(1) &lt; O(logn) &lt; O(n) &lt; O(n^2) &lt; O(2^n)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] git branch]]></title>
            <link>https://velog.io/@s_yeah/Git-git-branch</link>
            <guid>https://velog.io/@s_yeah/Git-git-branch</guid>
            <pubDate>Tue, 15 Feb 2022 14:40:25 GMT</pubDate>
            <description><![CDATA[<p>팀원들과 사이드 프로젝트를 한다면 필수적으로 알아야하는 git 사용법에 대해서 다뤄보고자 한다. 최근 친구와 사이드 프로젝트를 진행하면서 서로 각자 기능을 구현하고 합치려할 때 직접 파일을 넘겨주고 해당 파일을 어느 한쪽에 합치는게 불편하다고 느껴졌다. 매번 파일을 고치거나 새로 만들때마다 파일을 넘겨주면 어떤 파일이 최근파일인지 구별하기도 힘들고 합치는 과정도 하나의 task로 쌓여 피로도가 높아진다. </p>
<p>또는 이미 상용되고 있는 다른 프로젝트에 추가적으로 내가 원하는 기능을 구현해보고 싶을 때, 원본 프로젝트를 건들이지 않고 만들수 있는 기능이 필요하기도 하다. </p>
<p>이를 해결하기 위해 (각자 다른 버전의 코드를 관리해주기 위해...등) 알아야하는 개념이 <strong>git branch</strong>다.</p>
<h4 id="선행되어야-하는-지식들">선행되어야 하는 지식들</h4>
<ul>
<li>github과 git의 관계</li>
<li>repository - local과 remote의 차이</li>
</ul>
<h2 id="branch-기능-장점">Branch 기능 장점</h2>
<ul>
<li>한 소스코드로 동시에 다양한 작업 가능: 작업별로 소스코드를 사용하고 고치기 때문에 혹시나 오류가 났을 때도 해당 작업에 대한 히스토리를 해당 branch에서 한눈에 확인할 수 있어 대처 가능하다. </li>
<li>여러 브랜치를 사용해 브랜치를 오가며 작업 가능</li>
<li>다른 브랜치에 영향을 주지 않고 독립적으로 작업 가능: 각자의 브랜치에서 작성하다가 완성이 되면 합쳐진다. </li>
</ul>
<p align="center">
  <img src="https://images.velog.io/images/s_yeah/post/a4e329b7-754e-4840-a69b-b3df78a8ffa6/image.png" alt="여러브랜치의 git graph" width="number" />
여러 브랜치를 사용한 git graph (출처: Git Beginner's Guide for Dummies)
</p>
<br/>


<p>브랜치는 그 특징에 따라 나눠지는데 대체로 해당 작업의 특징에 따라 나눠진다. 
배포될 소스가 기록되는 브랜치는 통합 브랜치(Integration Branch)/ 기능 추가 및 버그 수정을 위한 브랜치는 피처 브랜치(Feature Branch, Topic Branch)로 해당 브랜치에서 작업을 완료하면 통합 브랜치에 병합하여 사용한다. </p>
<h2 id="git-branch-명령어">Git Branch 명령어</h2>
<table>
<thead>
<tr>
<th align="left"><center>기능</center></th>
<th align="left"><center>git 명령어</center></th>
</tr>
</thead>
<tbody><tr>
<td align="left">새로운 브랜치 생성</td>
<td align="left"><code>$ git branch 브랜치이름</code></td>
</tr>
<tr>
<td align="left">새로운 브랜치 생성 후 브랜치 전환</td>
<td align="left"><code>$ git switch -c 브랜치이름</code> <br/> 또는 <br/> <code>$ git checkout -b 브랜치이름</code></td>
</tr>
<tr>
<td align="left">브랜치 목록 확인</td>
<td align="left"><code>$ git branch</code></td>
</tr>
<tr>
<td align="left">브랜치 목록과 각 브랜치의 최근 커밋 확인</td>
<td align="left"><code>$ git branch -v</code></td>
</tr>
<tr>
<td align="left">브랜치 삭제</td>
<td align="left"><code>$ git branch -d 삭제할 브랜치이름</code></td>
</tr>
<tr>
<td align="left">병합하지 않은 브랜치 강제 삭제</td>
<td align="left"><code>$ git branch -D</code></td>
</tr>
<tr>
<td align="left">브랜치 전환</td>
<td align="left"><code>$ git branch switch 브랜치이름</code> <br/> 또는 <br/> <code>$ git checkout 브랜치이름</code></td>
</tr>
<tr>
<td align="left">브랜치 병합 <br/> (master 브랜치로 전환 후 병합)</td>
<td align="left"><code>$ git checkout master</code> <br/> <code>$ git merge dev</code></td>
</tr>
<tr>
<td align="left">로그에 모든 브랜치를 그래프로 표현</td>
<td align="left"><code>$ git log --branches --graph --decorate</code></td>
</tr>
<tr>
<td align="left">아직 커밋하지 않은 작업을 스택에 임시 저장</td>
<td align="left"><code>$ git stash</code></td>
</tr>
</tbody></table>
<br/>

<h2 id="git-branch를-활용한-workflow">Git branch를 활용한 workflow</h2>
<p>프로젝트를 진행하기에 앞서 팀원들이 각자 맡은 파트의 개발을 하기 위해서는 각각의 branch를 파서 개발을 진행해야 한다. branch를 만들고 작업하는 과정을 자세히 살펴보자.</p>
<p align="center">
  <img src="https://images.velog.io/images/s_yeah/post/f486e427-b1e5-4acb-a886-f33b019538a9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-15%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.07.31.png" alt="git repository flow"/>
  진행할 프로젝트의 repository
</p>

<p>프로젝트의 remote repository에서 fork를 해 나의 remote repository로 가져온다. 그런 후 코드 작업을 위해 local repository로 git clone 명령을 통해 소스코드를 가져온다.</p>
<p>local repository로 git clone을 해왔다면 이제 브랜치를 파는 작업을 하면 된다. </p>
<h3 id="featlogin-브랜치-생성">feat/login 브랜치 생성</h3>
<p>위의 그림과 같이 remote repository에서 main, dev 브랜치만 있던 저장소를 local repository로 clone해온 후 login기능을 구현한 개발을 하기 위해 feat/login 브랜치를 새로 생성하려 한다.</p>
<h3 id="featlogin-oauth-브랜치-생성">feat/login-oauth 브랜치 생성</h3>
<p>로그인 기능을 구현하다가 oauth인증을 받아 login하는 기능을 구현하고 싶을 때, 기존에 feat/login 브랜치에서 만들어둔 코드를 건드려 망가지게 할 수는 없으므로 feat/login-oauth 브랜치를 따로 만들어 준다. </p>
<h3 id="featlogin-브랜치에서-merge">feat/login 브랜치에서 merge</h3>
<p>feat/login-oauth 브랜치에서 기능 구현이 완료되었다면 작성한 코드를 합치기 위한 과정이 필요하다. feat/login-oauth에 있었던 브랜치에서 feat/login 브랜치로 다시 <code>git checkout feat/login</code> 한다.</p>
<p align="center">
<img src="https://images.velog.io/images/s_yeah/post/0b1d3bff-8f53-4f58-a356-39ab3ec8da18/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-15%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.53.30.png" alt="git local repository branch">
 feat/login에서 feat/login-oauth 브랜치를 생성하고 merge
</p>

<ul>
<li><p>feat/login 브랜치에서 따로 commit한 내역이 있는 경우
feat/login-oauth 브랜치를 합치면서 겹치는 부분은 없는지 확인 하고 merge</p>
</li>
<li><p>feat/login 브랜치에서 따로 commit한 내역이 없을 경우</p>
</li>
<li><p><em>fast-foward*</em> 방식으로 feat/login 브랜치에서 바로 feat/login-oauth 브랜치를 merge. (위의 그림 참고)</p>
<blockquote>
<p>이 경우를 빨리 감기 merge라고 하며 feat/login-oauth 브랜치는 login 브랜치와 같은 히스토리가 들어있기 때문에, 따로 수정사항(commit)이 없었다면, feat/login의 최신상태를 feat/login-oauth가 합쳐진 상태로 빨리 감아주는 것이다. </p>
</blockquote>
</li>
</ul>
<h3 id="merge한-featlogin-브랜치를-push">merge한 feat/login 브랜치를 push</h3>
<p>merge가 완료된 feat/login 브랜치는 내 remote repository로 push한다. </p>
<p align="center">
  <img src="https://images.velog.io/images/s_yeah/post/0cde8a0f-470d-46a7-8ec0-b51094838ccb/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-15%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.42.16.png" alt="git push merged branch"/>
     origin local에서 origin remote repository로 push된 feat/login 브랜치
</p>

<p>이때 명령어는 <code>git push origin feat/login(푸시하고자 하는 브랜치이름)</code>. </p>
<h3 id="원본-프로젝트로-push한-브랜치-pull-requestpr">원본 프로젝트로 push한 브랜치 pull request(PR)</h3>
<p align="center">
<img src="https://images.velog.io/images/s_yeah/post/7fed1140-7b4c-4b8e-b59e-6fdb8c6ba3e3/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-15%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.55.18.png" alt=""/>
push된 브랜치를 원본 프로젝트에 pull request
</p>

<p>새로운 브랜치를 파서 login 기능을 만든 후에 원래 프로젝트에 넣고 싶다면 pull request로 원본 remote repository에 pull request를 보내 코드를 해당 프로젝트를 함께하는 팀원들과 확인도 가능하다. </p>
<p>만약 origin local에서 작업하던 도중 project remote repository에서 수정사항이 생긴다면 작업하고 있던 origin local repository로 pull해서 변경사항을 반영하여 그걸 바탕으로 진행해야 한다. </p>
<p>위의 과정을 전부 종합하면, <img src="https://images.velog.io/images/s_yeah/post/70ff826a-41ab-42a8-9cbd-369b5fec861d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-15%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.06.14.png" alt="한눈에 보는 git branch workflow">
다음과 같은 git branch workflow가 나오는 걸 확인할 수 있다. </p>
<h3 id="git-rebase">git rebase</h3>
<p>특정 시점으로 branch가 가리키는 곳을 변경하는 기능으로, 말그대로 branch base를 다시 정해주는 것이다. branch 통합의 개념으로 merge와 유사하나, merge와 달리 변경내역의 이력이 그대로 남아있지 않는다. 변경 로그를 더 깔끔하게 유지하고 싶을 때 사용하게 된다.</p>
<p align="center">
<img src="https://images.velog.io/images/s_yeah/post/20b8d574-2441-4a4a-a513-e06cf869b1df/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-15%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.27.21.png" alt="git rebase"/>
git rebase
</p>

<p>첫번째 feat/login(검은글씨)에서 feat/login-oauth가 분기되었으나 각각의 브랜치에서 commit 내역이 생겼다. 만약 이 상황에서 <code>git rebase feat/login feat/login-oauth</code>를 한다면 두 브랜치를 통합하기 위해,  가장 최신 커밋인 두번째 feat/login(빨간색글씨)로 base가 옮겨지면서 feat/login-oauth 브랜치도 두번째 시점으로 branch를 통합한다.</p>
<ul>
<li><p>git rebase로 커밋 수정하기
<code>git rebase -i HEAD~~</code> : HEAD(현재 브랜치 위치)에서 최근 2개까지의 commit내역을 보여준다.</p>
</li>
<li><blockquote>
<p>에디터가 열린다. </p>
</blockquote>
</li>
<li><blockquote>
<p><code>pick 커밋1(수정하려는 커밋)</code>에서 <code>pick</code>을 <code>edit</code>으로 수정하고 <code>:wq</code>로 편집기를 나온다 </p>
</blockquote>
</li>
<li><blockquote>
<p><code>git commit --amend</code>로 수정하려 했던 커밋을 편집기에서 수정한다</p>
</blockquote>
</li>
<li><blockquote>
<p> 커밋 수정을 마친뒤,<code>git rebase --continue</code>를 하면 수정이 완료된다.</p>
</blockquote>
</li>
<li><p>git rebase로 커밋 통합하기
<code>git rebase -i HEAD~~</code> : HEAD(현재 브랜치 위치)에서 최근 2개까지의 commit내역을 보여준다.
-&gt; 에디터가 열린다. 
-&gt; <code>pick 커밋2(수정하려는 커밋)</code>에서 <code>pick</code>을 <code>squash</code>으로 작성하고 <code>:wq</code>로 저장하고 편집기를 나온다 
-&gt; 커밋 통합에 관한 설명이 화면에 보여진다. 
-&gt; 통합 완료! (* git rebase는 커밋 로그는 합쳐지면서 수정되기 전 커밋 내역은 보여지지 않는다)</p>
</li>
</ul>
<br/>

<p>** +) VScode에서 git graph 보는 법**
extension에서 git graph 설치
<br/>
<br/>
추가적으로 학습 가능한 자료</p>
<blockquote>
<p><a href="https://learngitbranching.js.org/?locale=ko">게임으로 이해하는 git branch</a>
<a href="https://dangitgit.com/ko">Dangit, git!  깃 실수 연습</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 알고리즘] 단계별 문제풀이 Lv.4 while문]]></title>
            <link>https://velog.io/@s_yeah/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8B%A8%EA%B3%84%EB%B3%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Lv.4-while%EB%AC%B8</link>
            <guid>https://velog.io/@s_yeah/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8B%A8%EA%B3%84%EB%B3%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Lv.4-while%EB%AC%B8</guid>
            <pubDate>Mon, 03 Jan 2022 07:57:19 GMT</pubDate>
            <description><![CDATA[<h2 id="백준-10952번">백준 10952번</h2>
<h4 id="ab를-while문으로-출력하라">A+B를 while문으로 출력하라</h4>
<p>입력받은 두 수의 더한값을 출력하는 문제인데, for문 때의 문제와 다르게 첫번째 입력값으로 테스트 케이스의 개수가 정해져 들어오지 않는다. 즉, for문 돌릴 때 필요한 범위 제한 값이 없기 때문에 언제까지 반복문을 돌려야하는지 정확한 개수가 정해져 있지 않은 경우 while문을 사용한다.</p>
<p>while문은 조건문이 false가 되는 순간 while문을 빠져나온다.</p>
<p>따라서 다음과 같이 코드를 작성했는데, 틀렸다고 나왔다.</p>
<pre><code class="language-java">import java.util.*;

public class Q10952 {
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);

        int result = 1;
        //result = 1 (result 초기값은 1로 설정)
        //첫번째 입력: result = 1, num1 = 1, num2 = 1   
        //두번째 입력: result = 2, num1 = 2, num2 = 3
        //세번째 입력: result = 5, num1 = 0, num2 = 0
        // -&gt; 세번째 입력의 경우 0과 0의 입력이 들어오는 순간 while문에 들어가지 않고 while문을 빠져나와야 하는데, 두번째 입력의 result값이 5로 저장되어 있기 때문에 while문을 들어간 후 그 다음 네번째 입력 전에 while문을 빠져나온다.

        while(result != 0){
            //1
            //2
            int num1 = scan.nextInt();
            //1
            //3
            int num2 = scan.nextInt();

            //2
            //5
            result = num1 + num2;
            System.out.println(result);
        }

    }
}
</code></pre>
<p>위의 코드와 같이 처리할 경우 출력 결과가 마지막 0일때까지도 더한 값 0을 출력하기 때문이다.
하지만 0일때는 출력하지 않고 while문을 빠져나와야 하기 때문에 조정해줘야 한다.</p>
<pre><code class="language-java">public class Q10952 {
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);

        int result = 1;


        while(result != 0){
            int num1 = scan.nextInt();
            int num2 = scan.nextInt();

            result = num1 + num2;
            if(result != 0){
            //더한값이 0일때는 출력하지 않도록 조건 하나를 더 걸어준다.
                System.out.println(result);
            }
        }

    }
}</code></pre>
<p>결국은 조건을 하나 더 추가하여 출력예제와 똑같이 나오도록 했다. 문제를 통과했지만 찝찝한 점은... 이렇게 if조건 하나하나 덕지덕지 붙여가며 코딩하는건 좋지 않은 것 같아 while문 안에서만 해결할 수 있는 방법이 있는지 다시 생각해봐야 했다. </p>
<h2 id="백준-10951번">백준 10951번</h2>
<h4 id="a--b-출력">A + B 출력</h4>
<p>입력의 끝이 정해져 있지 않은 채로 scanner 입력을 받아 더한 값을 출력해준다.</p>
<p><img src="https://images.velog.io/images/s_yeah/post/4293584d-eb56-434e-a6c7-06f7174be724/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-01-03%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.02.31.png" alt="백준 10951번 예제"></p>
<p>다음과 같은 값들이 입력값으로 들어온다. 이때까지의 더하기 값 출력하기 문제들과 다른 점은 입력값이 언제 끝날지 모른다는 것이다. EOF(End of File)는 더이상 파일에서 데이터를 읽어들일 수 없을 때로 이를 활용할 수 있다. 끝나는 지점을 명확히 알기 어려운 경우에 필요하다.</p>
<pre><code class="language-java">public class Main {
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        //결과값 초기화
        int sum = 1;

        //hasNext() 함수는 scanner가 입력값을 읽어올 때, 다음으로 불러올 data가 있는지를 따져본다.
        //다음에도 값이 있다면 
        while(scan.hasNext()){
            //반복해서 다 더한 값을 출력해냄

            int a = scan.nextInt();
            int b = scan.nextInt();
            sum = a+b;
            System.out.println(sum);
        }
    }
}</code></pre>
<p>scanner를 사용해 입력값을 받았다면 hasNext()함수로 그 다음으로 올 문자열을 확인해 볼 수 있다. 만약 다음에 입력값이 또 있다면 hasNext()는 true값이 되어 while문을 돌게 된다. </p>
<h2 id="백준-1110번">백준 1110번</h2>
<h4 id="더하기-사이클">더하기 사이클</h4>
<pre><code class="language-java">
import java.util.*;
public class Q1110 {
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);

        int originNum = scan.nextInt();


        int newNum = originNum;
        int count = 0;
        //String mixedNumString = &quot;&quot;;
        //int mixedNumInt = 0;

        //while문 조건을 true 말고 직접 조절하려고,
        //while(mixedNumInt != originNum)으로 했는데 백준에서는 계속 틀렸다고 나온다
        //디버깅 결과는 동일하게 나옴


        while(true){
            //입력받은 수의 일의 자리수를 구함
            int digit2 = newNum % 10;
            //입력받은 수의 십의 자리수를 구함
            int digit1 = newNum / 10;

            int sum = digit1 + digit2;

            //처음에는 문자열로 바꿔서 숫자 하나씩 이어붙이는걸로 생각했다
            //숫자 -&gt; 문자열 -&gt; 숫자
            //과정이 복잡해짐
            // mixedNumString = Integer.toString(digit2) + Integer.toString(sum % 10);
            // mixedNumInt = Integer.parseInt(mixedNumString);


            //더 간단한 방법 - 정수에서 해결
            newNum = (digit2 * 10) + (sum % 10);
            count++;

            if(newNum == originNum){
                break;
            }
        }
        System.out.println(count);
    }
}</code></pre>
<p>처음에 생각한 문제 해결 방안은 자리수 하나씩 문자열로 변환하여 이어 붙인 후 다시 정수로 변환하는 방식으로 생각했다. </p>
<p>하지만 십의 자리수, 일의 자리수를 다 구했다면 이 둘을 합쳐서 두자리수를 곱셈식으로 나타낼 수 있기 때문에 더 간단한 방법으로 바꿀 수 있다. </p>
<p>다만, while문 조건 작성에 있어 의문점이 생겼다. 처음에 작성한 코드는 아래와 같았는데 디버깅은 결과가 문제 없이 나왔었다. 하지만 백준 채점 결과에서는 계속 틀렸습니다가 나와서 아무리 생각해도 이유를 알 수가 없다...예제로 나온 입력예제 다 넣어봤는데 결과값이 일치한다.</p>
<pre><code class="language-java">
처음에 작성했던 while문

 while(mixedNumInt != originNum){
            //입력받은 수의 일의 자리수를 구함
            int digit2 = newNum % 10;
            //입력받은 수의 십의 자리수를 구함
            int digit1 = newNum / 10;

            int sum = digit1 + digit2;


            //새로만들어진 수를 한 번 더 복사해서 사용했다
            //if 조건문 사용을 피하고 싶어서...
            mixedNumInt = (digit2 * 10) + (sum % 10);
            newNum = mixedNumInt;
            count++;

        }
        System.out.println(count);
</code></pre>
<p> 왜 틀렸는지가 나오면 좋을텐데 채점 기준을 다시 살펴봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 알고리즘] 단계별 문제풀이 Lv.3 for문]]></title>
            <link>https://velog.io/@s_yeah/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8B%A8%EA%B3%84%EB%B3%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Lv.3-for%EB%AC%B8</link>
            <guid>https://velog.io/@s_yeah/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8B%A8%EA%B3%84%EB%B3%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Lv.3-for%EB%AC%B8</guid>
            <pubDate>Thu, 23 Dec 2021 14:44:47 GMT</pubDate>
            <description><![CDATA[<p>백준 알고리즘 Lv.3 for문 문제들을 java로 풀어보자!</p>
<h2 id="백준-2739번">백준 2739번</h2>
<h3 id="구구단을-출력해라">구구단을 출력해라!</h3>
<p>구구단을 출력하는 문제는 워낙 유명하고 기본중에 기본이다. 백준 알고리즘에서 나온 문제 말고도 여러가지 방식으로 다양하게 구구단을 출력해보는 문제들도 많다. 그 유명한 구구클래스도 있지 않는가~</p>
<p><img src="https://images.velog.io/images/s_yeah/post/a13afd02-9332-46b1-bf63-f89ccbcf0164/image.png" alt="구구클래스"></p>
<p>차치하고, 가장 기본 구구단 출력 문제를 풀어보자.
<img src="https://images.velog.io/images/s_yeah/post/8ec39a46-6bdb-40ff-b3ce-29e49cfb4424/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2012.11.14.png" alt="백준2739번"></p>
<p>반복문에서는 어느 부분이 반복되는지를 살펴보면 되는데, 위의 문제에서는 1부터 9까지 곱해주는 수가 달라지고 곱셈 문장은 계속 똑같은 형식으로 출력되고 있는 것을 볼 수 있다.</p>
<p>이번에 문제풀면서 큰 문제는 없었지만 어째서인지 또 컴파일 에러가 난것...</p>
<p><img src="https://images.velog.io/images/s_yeah/post/502d6b94-c610-4082-a78b-0401bba3457b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2012.09.48.png" alt="2739번 컴파일 에러">
 javascript를 쓰다가 java를 쓰려니까 출력문에 <code>&#39;작은따옴표&#39;</code>가 아니라 <code>&quot;큰따옴표&quot;</code>를 써야한다는 것을..까먹었던 것! 저번 level2 if문 예제 풀때도 똑같은데서 컴파일 에러났는데 이번에도 또 그래서 다시 한번 적어둔다.</p>
<h2 id="백준-15552번">백준 15552번</h2>
<h3 id="빠른-더하기">빠른 더하기</h3>
<p><img src="https://images.velog.io/images/s_yeah/post/9088cec1-34e6-4b97-a13a-17cfd1d680ec/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.41.14.png" alt="백준 15552번"></p>
<p>java의 경우 BufferedReader와 BufferedWriter를 사용하여 빠른 더하기를 구한다. 보통은 입출력을 Scanner, System.out.println으로 하는데 buffer를 사용하는 것보다 속도가 느린편이라 빠른 속도의 연산 처리를 위해서는 buffer를 활용해 계산한다. 데이터가 많아질수록 버퍼를 이용해 데이터를 처리하면 더 빠르게 처리가 가능해진다. BufferedReader와 BufferedWriter 사용법을 알아보자~!</p>
</br>
BufferedReader가 Scanner와 다른 점은 속도에도 차이가 있지만, 1) 입력받은 값을 한줄(엔터) 단위로 인식하고 2) 무조건 문자열로 저장한다는 것이다. 

<pre><code class="language-java">BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

String line = br.readLine(); //줄단위로 입력값을 받음
int num = Integer.parseInt(line);  //문자열로 들어온 입력값을 정수로 형변환</code></pre>
<p>따라서 입력받은 값을 한줄 단위로 인식하기 때문에, 공백단위로 입력받는 scanner와는 다르게, 입력받은 데이터를 따로 가공해줘야 한다. 
그리고 강제로 타입 변환을 해서 int값으로 바꿔주어 사용 가능하다.</p>
<p><img src="https://images.velog.io/images/s_yeah/post/cb07800d-ecb8-49cb-826b-6993af7090e9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.12.43.png" alt="15552번 컴파일에러1"></p>
<p>코드를 쓰고 제출했더니 컴파일 에러가 났다. error: cannot find symbol 에러메세지가 나타내는 건 컴파일러가 식별자를 알아볼 수 없기 때문으로 대체로 선언한 적 없는 변수를 사용하거나 범위 바깥에 있는 변수를 사용할때 이런 에러가 난다.
이번에는 BufferedReader 자체를 인식하지 못하고 있는 것 같아서보니 입출력과 관련한 패키지를 가져오지 못했기 때문에 <code>import java.io.*</code> 입출력 패키지를 import 해온다. </p>
<p>BufferedReader와 Writer를 쓸 때는 try-catch로 에러처리를 반드시 해줘야 한다. 
그렇지 않으면, 또 컴파일 에러가 난다. 에러처리도 까먹지 않고 꼭..!
<img src="https://images.velog.io/images/s_yeah/post/72e2f151-d0f8-42a5-95ac-e346bc4bbff8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.23.48.png" alt="15552번 컴파일에러2"></p>
<p>컴파일 에러를 다 수정하고 드디어 통과.</p>
<pre><code class="language-java">import java.util.*;
import java.io.*;

public class Main{
    public static void main(String[] args){
        try{
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

            //제일 처음 입력값으로 들어오는 테스트 케이스 수 
            int total = Integer.parseInt(br.readLine());

            //1부터 입력받은 값까지 반복문으로 더하기
            for(int i=1;i &lt;= total;i++){
                //buffer는 문자로 값을 읽음(줄 단위)
                String s = br.readLine();
                //받은 문자열 공백 단위로 끊기 
                String arr [] = s.split(&quot; &quot;);
                //배열안에 저장된 값들을 모두 int로 변형시키고,
                int firstNum = Integer.parseInt(arr[0]);
                int secondNum = Integer.parseInt(arr[1]);

                //첫번째 수와 두번째 수를 모두 더한 값을 바로 bufferwrite으로 출력해준다
                bw.write(firstNum+secondNum+&quot;\n&quot;);

            }
            //버퍼에 남아있는 값을 다 내보낸다
            bw.flush();
            //버퍼를 닫아준다
            bw.close();
        }
        //반드시 예외처리를 해줘야 한다. 안그러면 컴파일 에러가 난다.
        catch(IOException e){
        }


    }
}</code></pre>
<h2 id="백준-11021번">백준 11021번</h2>
<h3 id="a--b-를-정리하여-출력하기">A + B 를 정리하여 출력하기</h3>
<p>입력받은 테스트 케이스를 더하여 그 결과값을 출력해주는 문제이다. 대신 출력할 때,
<img src="https://images.velog.io/images/s_yeah/post/a0d0c5dd-6536-44d9-b5b1-cd5f106f3055/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-22%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.40.25.png" alt="11021번 출력예시"></p>
<p>이렇게 Case #순서가 붙어야 한다.</p>
<p>그리고 문제푸는 방식과 상관없이 🙀😿 자꾸 틀리는 System.out.println 출력 🙀😿</p>
<p>처음에 썼던 코드는 더한 값을 출력하는게 아니라 프린트문에서 더해줬다.</p>
<pre><code class="language-java">//결과값을 바로 더해주면 되겠지?
 System.out.println(&quot;Case #&quot;+i+&quot;: &quot;+ firstNum + secondNum);

//출력 예시
11
23
34
98
52</code></pre>
<p>하지만 출력문에서 바로 더해줄경우 이미 문자열과 섞여서 출력이 되고 있었기 때문에 정수의 합이 아닌 정수를 문자열처럼 나란히 나열해줄 뿐이었다...</p>
<p>결국 정수 변수를 만들어 따로 더해주고 출력!</p>
<pre><code class="language-java">import java.util.*;

public class Q11021{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        //테스트 케이스의 개수 T값
        int total = scan.nextInt();

        for(int i=1; i &lt;= total; i++){
            int firstNum = scan.nextInt();
            int secondNum = scan.nextInt();
            //int sum = firstNum + secondNum;
            System.out.println(&quot;Case #&quot;+i+&quot;: &quot;+ firstNum + secondNum);
        }   
    }
}</code></pre>
<p>이런 쉬운 것에서도 자꾸 틀리지 않도록 까먹지 말고 잘 쓰자!</p>
<h2 id="백준-2438번-2439번">백준 2438번, 2439번</h2>
<p>별 찍기 문제로 별을 어디에 찍어주느냐에 따라 문제 유형이 다르다.</p>
<p>문제를 풀때 사용한 방법은 이중 for문이다.
입력 예제와 출력 예제를 보면, 정렬이 어느쪽으로 되어있는지의 차이만 있고 입력받은 N번째 줄까지 별을 찍어 출력하라는 문제다. </p>
<p>2438번은 <img src="https://images.velog.io/images/s_yeah/post/639f62ab-a89d-431b-a280-7c8c7a96d037/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-23%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.20.53.png" alt="2438번 출력예제">
2439번은<br><img src="https://images.velog.io/images/s_yeah/post/9567e4ee-9828-4231-9da3-729f9a71fd5e/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-23%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.19.04.png" alt="2439번 출력예제"></p>
<pre><code class="language-java">public class Q2438 {
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);

        int num = scan.nextInt();

        //입력받은 줄(N)만큼 반복
        for(int i=1;i&lt;=num;i++){
            //매 줄마다 찍을 별의 개수만큼 반복
            for(int j=0;j&lt;i;j++){
            //i=1이면, j=0부터 1미만으로 반복(*), 
            //i=2이면, j=0부터 2미만으로 반복(**)
            //...
                System.out.print(&quot;*&quot;);
            }
            //다음줄로 넘어가는 줄 변경
            System.out.println(&quot;&quot;);
        }


    }
}</code></pre>
<p>2438번은 기본적으로 출력이 왼쪽에서부터 찍혀나오는 <code>System.out.print</code>이기 때문에 별의 개수만 맞춰서 for문을 작성해주면 된다.</p>
<p>2439번의 경우에는 출력을 했을때 생기는 공백까지도 직접 출력을 해줘야 한다.</p>
<pre><code class="language-java">public class Q2439 {
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);

        int num = scan.nextInt();
        //입력받은 줄 수 만큼 반복하는 첫번째 for문
        for(int i=1;i&lt;=num;i++){
            //공백찍기
            //두번째 for문은 공백의 개수를 찍어내는 역할
            //공백은 줄이 넘어갈수록 개수가 줄어들어야 한다
            for(int j=num;j&gt;i;j--){
                System.out.print(&quot; &quot;);
            }
            //별찍기
            //공백이 찍힌 후 해당 줄에 찍힐 별
            //별은 줄수에 맞춰 그 개수만큼 찍혀야 한다
            for(int k=0;k&lt;i;k++){
                System.out.print(&quot;*&quot;);
            }
            //다음줄로 넘어감
            System.out.println();

        }
    }</code></pre>
<p>위를 활용한다면 다양한 별찍기를 시도해볼 수 있다!
거꾸로 줄어드는 별찍기도 출력이 가능해진다.
<img src="https://images.velog.io/images/s_yeah/post/e2a199d7-777d-4c01-9bf2-7c43ae82f88b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-23%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.32.31.png" alt="별찍기다른예제"></p>
<pre><code class="language-java">import java.util.*;

public class StarPrint {
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);

        int num = scan.nextInt();
        //입력받은 줄 수 만큼 반복하는 첫번째 for문
        for(int i=0;i&lt;num;i++){
            //두번째 for문은 공백의 개수를 찍어내는 역할
            //공백은 줄이 넘어갈수록 개수가 줄어들어야 한다
            for(int j=num;j&gt;i;j--){
                System.out.print(&quot;*&quot;);
            }

            System.out.println();

        }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 알고리즘] 단계별 문제풀이 Lv.2  if문]]></title>
            <link>https://velog.io/@s_yeah/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8B%A8%EA%B3%84%EB%B3%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Lv.2-if%EB%AC%B8</link>
            <guid>https://velog.io/@s_yeah/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8B%A8%EA%B3%84%EB%B3%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Lv.2-if%EB%AC%B8</guid>
            <pubDate>Tue, 14 Dec 2021 10:08:13 GMT</pubDate>
            <description><![CDATA[<p>단계별 문제풀이로 익히는 java</p>
<p>이번엔 if 조건문과 관련한 문제들</p>
<h2 id="백준-1330번">백준 1330번</h2>
<h3 id="두-수-비교하기">두 수 비교하기</h3>
<p><img src="https://images.velog.io/images/s_yeah/post/9d144057-7928-48b8-8a9f-7f7f4b590f73/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.30.13.png" alt="1330"></p>
<p>두가지 수를 입력받아 원하는 결과값을 if문에 따라 다르게 출력해준다. 대소비교에 따라 결과값이 달라지도록 한다.</p>
<pre><code class="language-java">import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        int a = sc.nextInt();
        int b = sc.nextInt();

        if(a &lt; b){
            System.out.println(&quot;&lt;&quot;);
        }else if(a &gt; b){
            System.out.println(&quot;&gt;&quot;);
        }else System.out.println(&quot;==&quot;);

    }
}</code></pre>
<h2 id="백준-2753번">백준 2753번</h2>
<h3 id="윤년-판별하기">윤년 판별하기</h3>
<p><img src="https://images.velog.io/images/s_yeah/post/99bd3464-4e1e-4a60-a6ce-22885e43288a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.45.49.png" alt="2753번"></p>
<p>윤년은 4와 400으로 나눠지면서 100으로는 나눠지지 않는 4자리수의 연도이다. 이를 if 조건문으로 작성해 입력으로 들어오는 4자리 정수의 윤년 여부를 판별해줘야한다.</p>
<p><img src="https://images.velog.io/images/s_yeah/post/34d35057-0454-44f6-9645-438c17f16321/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.50.21.png" alt="2753번 컴파일 에러"></p>
<p>위의 이미지처럼 조건문을 and와 or 연산자로 전부 하나의 if문 안에 넣어줬더니, 컴파일 에러가 났다. 왜 컴파일 에러가 났나 다시 자세히 살펴보니, <code>leap % 400 = 0</code>이라고 작성하여 에러가 났다. 이럴 경우 등호비교를 하는 것이 아니라 값을 넣어준다는 뜻인데 그 값이 400이라는 정수로 되어있기 때문에 당연히 성립이 될리가 없다. 따라서 <code>leap % 400 == 0</code>으로 바꿔주면 문제없이 통과한다. </p>
<p>또 다른 방법으로 else if를 사용할 경우에는 다음과 같이 작성한다. </p>
<pre><code class="language-java">import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        int leap = sc.nextInt();

        if(leap % 4 == 0 &amp;&amp; leap % 100 != 0){
            System.out.println(1);
        }else if(leap % 400 == 0){
            System.out.println(1);
        }else {
            System.out.println(0);
        }

    }
}</code></pre>
<p>이럴 경우에도 문제없이 통과된다. </p>
<h2 id="백준-2884번">백준 2884번</h2>
<h3 id="알람시계">알람시계</h3>
<p><img src="https://images.velog.io/images/s_yeah/post/ec0aaf3a-5c43-4a9a-b795-0b9a2bf2d5e3/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.36.50.png" alt="2884번"></p>
<p>문제가 길지만 요약하자면 Hour와 Minute을 입력받아 45분 전 값을 출력하는 문제이다.</p>
<p>이 문제를 풀때는 어떤 규칙이 있는지를 생각해보고 edge case를 생각해 if문으로 따로 조건을 걸어주었다. </p>
<p>1)첫번째로는 minute과 관련해 어떻게 변하는지를 따진다.</p>
<p>45분 이상으로 숫자가 클경우에는 45분전으로 시간을 돌리는게 어렵지 않다. 입력받은 Minute의 값에서 45분을 빼주면 되기 때문이다. </p>
<p>하지만 45분보다 작을경우에는 Minute값만 바꿔주어서는 안되고, Hour도 1씩 줄어야 한다. 혹여 2를 빼주는 경우도 생각을 해봤으나 최솟값인 0분에서 45분 전으로 돌린다하면 (hour-1)시 (15)분이기 때문에 -1만 해주면 된다. </p>
<br>
2)두번째로는 hour와 관련해서 어떤 변화가 생기는지를 따진다.

<p>0시의 경우 -1을 했을 때 -1이 아닌 23시로 변하는 것까지 조건문안에서 반영해줘야 한다. </p>
<p>이러한 조건을 모두 생각하고 if문을 작성한다면 다음과 같다.</p>
<pre><code class="language-java">/* 백준알고리즘 2884번 문제
시간과 분을 45분 전으로 설정하여 출력하기
*/
import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);

        int hour = scan.nextInt();
        int minute = scan.nextInt();

        //minute이 45분보다 작은경우, hour에 변화가 생김
        if(minute &lt; 45){
            //시간이 1~23일때
            if(hour != 0){
                //시간이 -1 줄어든다
                hour--;
                //60분 중에서 (45 - minute)분한 시간을 빼준다
                minute = 60 - (45 - minute);
                //옳은 출력방식
                System.out.println(hour+&quot; &quot;+minute);
            }
            //시간이 0일때
            else if(hour == 0){
                //23시로 바꿔줘야한다
                hour = 23;
                minute = 60 - (45 - minute);
                System.out.println(hour+&quot; &quot;+minute);
            }
        }else{//minute이 45분보다 클때, hour는 그대로
            minute = minute - 45;
            System.out.println(hour+&quot; &quot;+minute);
        }
    }
}
</code></pre>
<p>+) 참고로 처음 시도했을 때는 컴파일 에러가 났었다. 이유는 출력하는 문장을 잘못썼기 때문에. 오랜만에 java로 연습하니 자꾸 기본적인 문장에서 틀린다. </p>
<p>처음 내가 작성한 출력문은 <code>System.out.println(&quot;%d %d&quot;, hour, minute);</code>  문자열로써 출력하는 문장이었다. 하지만 문제에서 원하는 출력값은 정수 그대로 출력하는 것이었기 때문에 컴파일 에러가 난거 같다. </p>
<p>따라서 여러 시행착오를 거쳐 (<code>System.out.println(hour, minute);</code>를 해보기도...) 출력해야하는 값에 맞는 형식으로 <code>System.out.println(hour+&quot; &quot;+minute);</code> 출력해준다.</p>
<p>++) 제대로 풀었다고 생각했는데 자꾸 정답이 틀렸다고 나왔음. 왜그런가 싶어서 계속 보는데...</p>
<p>처음 썼던 코드 👎</p>
<pre><code class="language-java">if(minute &lt; 45){
            //시간이 1~23일때
            if(hour != 0){
                //시간이 -1 줄어든다
                hour = hour - 1;
                //60분 중에서 (45 - minute)분한 시간을 빼준다
                minute = 60 - (45 - minute);
                //옳은 출력방식
                System.out.println(hour+&quot; &quot;+minute);
            }
            //시간이 0일때
            if(hour == 0){
                //23시로 바꿔줘야한다
                hour = 23;
                minute = 60 - (45 - minute);
                System.out.println(hour+&quot; &quot;+minute);
            }</code></pre>
<p> hour를 나누는 조건문 if에서 if-else if문이 아니라 if-if문으로 작성을 하여 문제가 생기는 것이었다. 
 예를들면, hour가 1이고 minute이 30일 때, 첫번째 조건문 <code>if(minute &lt; 45)</code>를 만족하여 조건문 안으로 들어온다.
hour가 1이니까 그 다음 조건문 <code>if(hour !=0)</code>를 통과하여 <code>hour = hour - 1;</code> 문장을 수행한다. 
그러면 hour는 0으로 값이 변하는데 여기서!!!문제가 발생한다.
hour는 0으로 값이 변한 상태인데 <code>if(hour != 0)</code>문을 빠져나오면 그 다음 <code>if(hour == 0)</code>문을 만나게 된다. 
그러면 의도와 다르게 if 조건문 안으로 들어가버린다. </p>
<p>이렇게 변수를 내가 수정하는 경우에는 해당하는 if 조건문에 잘 들어와있는지, 다른 조건문으로 들어가지는 않는지 따져봐야한다.</p>
<p>따라서 위의 문제를 해결하려면 else if문으로 작성해줘야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] git push origin main 에러]]></title>
            <link>https://velog.io/@s_yeah/Git-git-push-origin-main-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@s_yeah/Git-git-push-origin-main-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Tue, 14 Dec 2021 08:03:27 GMT</pubDate>
            <description><![CDATA[<br>
<center>별거 아니라서 안될때 더 짜증나는 git 에러</center>


<p>git을 쓰다보면 별거 아닌거 같은데 오류가 자주 발생하여 매번 검색해야하는 경우가 많다. </p>
<br>


<p>최근에 git이 주 브랜치를 master가 아니라 main으로 설정해두어서 습관처럼 <code>git push origin master</code>로 명령어를 써서 commit을 해버리면 내가 원하지 않는 master 브랜치가 새로 생겨버린다.</p>
<p>그래서 <code>git push origin main</code>으로 commit을 하려 했더니,
<img src="https://images.velog.io/images/s_yeah/post/52af2ecd-49a7-48d3-bf18-48d7185ccb28/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.37.05.png" alt="git push 에러"></p>
<pre><code>Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: &#39;git pull ...&#39;) before pushing again.</code></pre><p>이러한 오류가 떴다. 
자세히 보니 원격 저장소에서 변경사항이 생겼는데 그걸 pull해준 다음에 다시 push하라는 말이었다.
알고보니 github 페이지에서 직접 readme파일을 수정하고 저장했는데 pull을 해주지 않아서 그런거였다. </p>
<p>그래서 <code>git pull</code>을 해줬더니 또 오류가 났다.
<img src="https://images.velog.io/images/s_yeah/post/3623981a-acfd-44e5-abd6-8c8b25696856/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.08.25.png" alt="git pull 오류"></p>
<p>보니까 명령어를 잘못써준거였다.</p>
<br>

<p>다시 제대로 써주자
<img src="https://images.velog.io/images/s_yeah/post/1e28fc0a-3ef3-4cb4-a443-35d6d9ce55ce/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.15.03.png" alt="git pull"></p>
<p><code>git pull origin main</code>을 해주면 편집기로 넘어가서 메세지를 작성해주고 빠져나오면 된다. 편집기에서 작성할때는 <code>i</code>를 입력하면 원하는 메세지를 입력할 수 있다. 다 입력했으면 <code>esc</code>누르고 <code>:wq</code> (write &amp; quit)으로 빠져나온다. 
<img src="https://images.velog.io/images/s_yeah/post/8302e211-1df4-4b82-a7f6-a9e997f4acf6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.12.55.png" alt="merge 메세지">
이제 정상적으로 변경된 README.md 파일이 main branch에 merge된 것을 볼 수 있다. </p>
<p>이제 드디어~ 원래 하려고 했던 <code>git push origin main</code>을 정상적으로 수행할 수 있다. (물론 <code>git commit</code>까지 완료한 후)</p>
<p><img src="https://images.velog.io/images/s_yeah/post/0aad3b7a-49bb-42aa-a811-da38bb01989f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-12-14%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.15.18.png" alt="git push origin main"></p>
<p>성공적으로 main 브랜치에 push 된 것을 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Toy Project] 웹서버 구축하기 AWS, Apache]]></title>
            <link>https://velog.io/@s_yeah/Toy-Project-%EC%9B%B9%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-AWS-Apache</link>
            <guid>https://velog.io/@s_yeah/Toy-Project-%EC%9B%B9%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-AWS-Apache</guid>
            <pubDate>Tue, 14 Dec 2021 06:25:40 GMT</pubDate>
            <description><![CDATA[<h2 id="아마존-aws-서비스에-가입-후-인스턴스-생성">아마존 aws 서비스에 가입 후 인스턴스 생성</h2>
<p>가입해서 인스턴스 생성까지 참고했던 링크 
단계별로 아마존에서 인스턴스를 생성하는데까지 자세하게 나와있어서 참고하여 만들면 된다.</p>
<blockquote>
<p>아마존 인스턴스 생성하는 법 
<a href="https://velog.io/@hotoron/AWS-%EB%AC%B4%EB%A3%8C%EC%84%9C%EB%B2%84-%EC%82%AC%EC%9A%A9%EB%B2%95-%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4">https://velog.io/@hotoron/AWS-%EB%AC%B4%EB%A3%8C%EC%84%9C%EB%B2%84-%EC%82%AC%EC%9A%A9%EB%B2%95-%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4</a></p>
</blockquote>
<p>참고로 나는 인스턴스 생성시 서버를 ubuntu로 설정하고 생성했다. 그래서 서버 만드는데 정보찾기가 조금 어려웠다. amazon linux로 하면 정보가 더 많으니 그걸 참고해도 좋을 것 같다.</p>
<h2 id="인스턴스-생성-후-탄력적-ip-연결까지-하고-서버에-접속하기">인스턴스 생성 후 탄력적 IP 연결까지 하고 서버에 접속하기</h2>
<p>내가 만든 서버에 접속하기 위해서는 처음 인스턴스 생성시 받은 pem키가 필요하다. 터미널에서 ssh 폴더에 접속이 안되어도 좌절하지 말자. 수많은 터미널 접속 오류가 떴지만 결국 대체 방안은 있다!
Mac인 경우 finder 켜서 <code>User/username(본인의 컴퓨터 이름)</code> 경로에 들어가서 <code>cmd + shift + .</code> 이렇게 입력하면 숨겨진 폴더가 보이는데 다운로드 받은 pem 키를 <code>User/username(본인의 컴퓨터 이름)/.ssh</code> 폴더에 옮겨 넣어주면 된다. </p>
<p>만약 다운로드 폴더에 pem키가 그대로 있다면 보안을 위해 pem키는 따로 usb에 보관해두도록 하자. 만약 키를 잃어버린다면 꽤 복잡한 절차를 거쳐야한다고 하니..</p>
<blockquote>
<p>우분투 서버 접속하기
<a href="https://soobarkbar.tistory.com/223">https://soobarkbar.tistory.com/223</a></p>
</blockquote>
<p>pem key의 권한을 변경해주기 위해 다음과 같은 명령어를 터미널에 작성해준다. 
<code>$ chmod 600 ~/.ssh/pemKeyName.pem</code>
권한 변경 후 다음과 같은 명령어를 입력해 서버에 접속하게 한다. ubuntu 인스턴스이기 때문에 유저이름을 ubuntu로 적어줘야한다.(아래에 서버 접속시 오류났던 부분 정리 참고!)</p>
<p><code>$ ssh -i ~/.ssh/pemKeyName.pem ubuntu@퍼블릭IP주소(ec~~.ap-northeast-2.compute.amazonaws.com)</code></p>
<p>다음과 같이 명령어를 입력해주면 내가 생성한 ubuntu 인스턴스에 접속할 수 있게 된다. </p>
<h3 id="간결한-명령어로-서버-접속">간결한 명령어로 서버 접속</h3>
<p>저 긴 명령어(<code>$ssh -i ~~</code>)를 입력하고 싶지 않다면 <code>~/.ssh/config</code> 파일을 생성해 vi 편집기로 내용을 추가해주는 방법이 있다. 아래의 방법대로 명령어를 작성해본다면 쉽게 설정 가능하다.</p>
<p>인스턴스 생성시 다운받았을 때 생성된 pem 키를 user/.ssh/ 폴더에 넣어줬다면,
서버 접속하기 방법과 똑같이 pem key의 권한을 바꿔준다
<code>$ chmod 600 ~/.ssh/pemKeyName.pem</code></p>
<p>권한 변경 후 vi 편집기로 config파일을 생성해 내용을 작성해준다.
<code>$ vi ~/.ssh/config</code>
편집기로 들어가면 다음과 같이 내용을 작성해준다. 편집기 작성은 i를 눌러 insert 모드로 바꿔주고 다 작성하면 <code>:wq</code>를 써서 저장 후 빠져나온다. </p>
<pre><code>Host 서비스 명
HostName 퍼블릭 탄력적IP주소
User ubuntu(선택한 인스턴스에 따라 달라짐)
IdentityFile ~/.ssh/pemKeyName.pem</code></pre><p>위와 같이 설정했다면 <code>$ ssh ubuntu@serviceName(config에서 Host 서비스명으로 적었던 그 서비스 명)</code>으로 간결하게 서버에 접속이 가능해진다. </p>
<h4 id="서버-접속-시-오류-났던-부분">서버 접속 시 오류 났던 부분</h4>
<p>아무래도 잘 모른채로 서버에 접속하려고 하다보니까 똑같이 따라 썼는데도 계속 오류가 나서 답답했던 경우가 있었다. 숱한 구글링 끝에 발견한 오류의 이유..!
우분투 ami인 경우에는 사용자명이 <code>ubuntu</code>였던 것이다. 어쩐지 분명히 pem.key 경로도 제대로 썼는데 자꾸 <code>permission denied</code>가 떠서 머리를 싸맸는데 생각보다 간단하게 해결 가능한 오류였다.
<img src="https://images.velog.io/images/s_yeah/post/51db5fe2-dee9-447b-95de-ab5fe640b719/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-27%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.23.21.png" alt="유저이름 오류캡쳐"></p>
<p>제대로 사용자명을 바꿔준 후 다시 입력하면 우분투 환경으로 들어가진다. </p>
<p><img src="https://images.velog.io/images/s_yeah/post/c8a89643-c672-409e-9ac8-fe10d5926751/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-27%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.23.54.png" alt=""></p>
<p>ubuntu 서버에 접속하고 난 후에는 apache를 설치하고 입력해주면 된다.</p>
<h2 id="apache-설치">Apache 설치</h2>
<blockquote>
<p>apache 설치하는 법
<a href="https://trycatching.tistory.com/46">https://trycatching.tistory.com/46</a></p>
</blockquote>
<p>참고한 링크에 보면 자세하게 apache 업데이트된 버전을 설치할 수 있다.</p>
<blockquote>
<p>현재 apache 서버 구동 상태확인 및 서버 시작, 종료에 대한 명령어 관련 링크
apache 설치 및 버전확인 할때 
<a href="https://tomcabin.tistory.com/8">https://tomcabin.tistory.com/8</a></p>
</blockquote>
<p>+) <code>sudo systemctl status apache</code>로 실행할 때 running 상태를 알려주는 페이지가 나왔는데 이게 편집프로그램으로 넘어간줄 모르고 너무도 당황해버렸다. 구글링을 해도 그 페이지에서 어떻게 넘어가는지를 설명안해줘서 별것도 아닌걸로 막히는거 같아서 자괴감 들고 괴로웠지만!! 보다보니 vi 편집기 페이지와 비슷해서 q를 눌렀더니 너무 간단하게 해결. 이러니 검색을 아무리 해도 안나오지..왜냐면 아무도 모를거라고 생각안하니까..하지만 나같은 초보는 이런거 하나까지 알려줘야 기본을 겨우 따라가니까 여기에라도 적어둔다.</p>
<br>
<br>
apache에서 제공하는 기본 default값인 index.html 말고 내가 원하는 html 파일을 띄우기 위해서는 ubuntu 서버에 폴더를 하나 만들어 프로젝트 중인 html 파일을 올려두어야 한다. 

<p>apache에서 제공하는 index.html 화면은 다음과 같으며 서버 접속이 성공적으로 됐을 경우 보이는 화면이다.
<img src="https://images.velog.io/images/s_yeah/post/3512d8d6-98b8-4cd3-8dd4-9864aa01bf69/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-27%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.52.40.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP/네트워크] REST API]]></title>
            <link>https://velog.io/@s_yeah/HTTP%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-REST-API</link>
            <guid>https://velog.io/@s_yeah/HTTP%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-REST-API</guid>
            <pubDate>Tue, 23 Nov 2021 10:20:25 GMT</pubDate>
            <description><![CDATA[<p>REST는 &quot;Representational State Transfer&quot;의 약자로 http 웹의 장점을 최대로 활용할 수 있는 아키텍쳐이다. RESTful한 API를 작성하기 위해서는 0-3단계로 총 4단계 충족되어야 하는데 보통 2단계까지만 충족되어도 잘 작성되어진 API라고 한다.</p>
<ul>
<li><p>0단계 </p>
</li>
<li><p>1단계 
개별 리소스와의 통신을 준수해야한다. 즉 구체적으로 자원 요청해야한다. 단순히 자원이 있다, 없다만을 판단할 수 있는 요청을 보내기보다는 어느 자리에 내가 원하는 리소스가 정확히 있는지를 물어보는 요청을 할 수 있어야 한다.</p>
</li>
<li><p>2단계
CRUD(Create, Read, Update, Delete)를 맞춰 적절한 HTTP 메서드를 사용할 수 있어야 한다.</p>
<blockquote>
<p><strong>멱등성(idempotent)</strong>
서버 리소스가 변화되어 같은 작업을 실행해도 다른 결과값이 나올때는 멱등성이 없다고 판단한다.
HTTP 메세지 중 POST의 경우, 예약프로그램을 예시로, 사용자가 신청서에 맞게 한번 예약을 한 경우(POST) 같은 사용자가 같은 요청을 또 보냈을 때는 다른 결과가 나오게 된다. 첫번째 예약을 했을 때는 &quot;예약이 완료되었습니다.&quot;라는 문구의 결과가 나올 것이고 예약 현황을 가지는 리소스에는 사용자가 요청한 데이터가 쌓이게 될 것이다. 
그리고 똑같이 두번째 예약을 진행할 때는 &quot;이미 예약이 되어 있어 더이상 예약이 불가합니다.&quot;라는 결과가 나오면서 멱등성이 성립되지 않는다. 원래 서버가 가지고 있던 &#39;예약 현황&#39; 리소스가 변경되었기 때문에 다른 결과값이 나올 수 밖에 없는 상황이 생겼기 때문이다. </p>
</blockquote>
</li>
</ul>
<p>API 작성이 어렵다면 다른 API 작성 기준이 어떻게 되는지를 확인해보면 도움이 된다.</p>
<p>호주 정부 API 작성 가이드
<a href="https://api.gov.au/standards/national_api_standards/">https://api.gov.au/standards/national_api_standards/</a></p>
<p>구글 API 작성 가이드
<a href="https://cloud.google.com/apis/design?hl=ko">https://cloud.google.com/apis/design?hl=ko</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTTP/네트워크] 기초]]></title>
            <link>https://velog.io/@s_yeah/HTTP%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@s_yeah/HTTP%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Tue, 23 Nov 2021 10:19:52 GMT</pubDate>
            <description><![CDATA[<h3 id="클라이언트-서버-콘셉트">클라이언트-서버 콘셉트</h3>
<ul>
<li><p><input disabled="" type="checkbox">  클라이언트-서버 아키텍처
: &#39;2 tier architecture&#39;로도 불리는 클라이언트-서버 아키텍쳐는 실시간으로 이용자에게 정보를 전달하기 위해 필요한 구조로서 리소스와 리소스를 사용하는 앱을 분리한다. <br></p>
</li>
<li><p><em>예시)*</em> 서점에서 책을 구매하려 할 때,</p>
<table>
<thead>
<tr>
<th align="center">이용자(클라이언트) <code>-----------</code> 서점 직원 (서버)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">요청: <code>---------</code> <code>--(책주문)--&gt;</code> <code>------------</code></td>
</tr>
<tr>
<td align="center">응답: <code>---------</code> <code>&lt;--(책판매)--</code> <code>------------</code></td>
</tr>
<tr>
<td align="center">위와 같이 &#39;책 주문&#39;이라는 클라이언트의 요청이 발생할 때 그에 맞는 응답을 클라이언트에게 보내주는 게 서버의 역할이다.</td>
</tr>
<tr>
<td align="center">만약 &#39;3 tier architecture&#39;일 경우에는 리소스를 따로 저장해두는 데이터베이스가 존재하여 다음과 같은 구조로 통신을 주고 받는다.</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="center">이용자(클라이언트)</th>
<th align="center"><code>-------------</code></th>
<th align="center">서점 직원 (서버)</th>
<th align="center"><code>------------</code></th>
<th align="center">데이터베이스</th>
</tr>
</thead>
<tbody><tr>
<td align="center">요청: <code>-----------</code></td>
<td align="center"><code>---(책주문)---&gt;</code></td>
<td align="center"><code>-----------</code></td>
<td align="center"><code>---(책조회)--&gt;</code></td>
<td align="center"><code>-----------</code></td>
</tr>
<tr>
<td align="center">응답: <code>-----------</code></td>
<td align="center"><code>&lt;---(책판매)---</code></td>
<td align="center"><code>-----------</code></td>
<td align="center"><code>&lt;---(책정보)--</code></td>
<td align="center"><code>-----------</code></td>
</tr>
<tr>
<td align="center">데이터 베이스에는 앱사용에 필요한 리소스들을 따로 저장해둔다. 그렇기 때문에 서버에서 필요한 리소스를 데이터 베이스에서 찾아 클라이언트로 가져다 준다.</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
</li>
<li><p><input disabled="" type="checkbox">  HTTP를 이용한 클라이언트-서버 통신과 API
올바른 통신을 위해서는 &#39;프로토콜&#39;이 필요하다. 프로토콜은 일종의 규약으로 서버와 클라이언트가 주고받는 통신을 가능하게 만든다. 예를 들어 도서관에서 책을 찾을 때 필요한 기본 정보나 입력 양식 등이 정해져 있는 규약으로 가장 기본 양식을 지켜야 하는 것처럼, 서버와 클라이언트 간의 통신에서도 그러한 규약, 규칙이 필요하다. 
웹 애플리케이션에서는 통신할 때 http 프로토콜을 사용한다. http 메세지를 주고 받으며 요청과 응답을 처리한다. </p>
</li>
<li><p>프로토콜의 종류</p>
<table>
<thead>
<tr>
<th align="center">프로토콜 이름</th>
<th align="left">설명</th>
<th align="center">계층</th>
</tr>
</thead>
<tbody><tr>
<td align="center">HTTP</td>
<td align="left">웹에서 HTML, JSON 등의 정보를 주고받는 프로토콜</td>
<td align="center">7) 응용계층</td>
</tr>
<tr>
<td align="center">HTTPS</td>
<td align="left">HTTP에서 보안이 강화된 프로토콜</td>
<td align="center">7) 응용계층</td>
</tr>
<tr>
<td align="center">FTP</td>
<td align="left">파일 전송 프로토콜</td>
<td align="center">7) 응용계층</td>
</tr>
<tr>
<td align="center">SMTP</td>
<td align="left">메일을 전송하기 위한 프로토콜</td>
<td align="center">7) 응용계층</td>
</tr>
<tr>
<td align="center">SSH</td>
<td align="left">CLI 환경의 원격 컴퓨터에 접속하기 위한 프로토콜</td>
<td align="center">7) 응용계층</td>
</tr>
<tr>
<td align="center">RDP</td>
<td align="left">Windows 계열의 원격 컴퓨터에 접근하기 위한 프로토콜</td>
<td align="center">7) 응용계층</td>
</tr>
<tr>
<td align="center">WebSocket</td>
<td align="left">실시간 통신, Push 등을 지원하는 프로토콜</td>
<td align="center">7) 응용계층</td>
</tr>
<tr>
<td align="center">TCP</td>
<td align="left">HTTP,FTP 통신의 근간이 되는 프로토콜</td>
<td align="center">4) 전송계층</td>
</tr>
<tr>
<td align="center">UDP</td>
<td align="left">양방향 TCP와 다르게 단방향으로 단순하고 삐르지만 신뢰가 낮은 인터넷 프로토콜</td>
<td align="center">4) 전송계층</td>
</tr>
</tbody></table>
</li>
</ul>
<h3 id="브라우저의-작동-원리">브라우저의 작동 원리</h3>
<p>보이지 않는 곳의 통신</p>
<ul>
<li><input disabled="" type="checkbox"> URL과 URI의 차이를 이해할 수 있다.</li>
<li><input disabled="" type="checkbox"> IP 주소와 PORT에 대해 이해할 수 있다.</li>
<li><input disabled="" type="checkbox"> DNS와 IP 주소의 관계를 설명할 수 있다.</li>
<li><input disabled="" type="checkbox"> 크롬 브라우저의 에러 메시지를 통해 문제를 파악할 수 있다.</li>
</ul>
<p>보이는 곳의 통신</p>
<ul>
<li><input disabled="" type="checkbox"> AJAX의 개념을 이해할 수 있다.</li>
<li><input disabled="" type="checkbox"> SSR과 CSR의 차이를 이해할 수 있다.</li>
<li><input disabled="" type="checkbox"> CORS의 개념을 이해할 수 있다.</li>
</ul>
<h3 id="http-messages의-구조">HTTP messages의 구조</h3>
<ul>
<li><input disabled="" type="checkbox"> HTTP의 동작 방식을 이해할 수 있다.</li>
<li><input disabled="" type="checkbox"> HTTP requests와 responses를 구분할 수 있다.</li>
<li><input disabled="" type="checkbox"> HTTP의 응답 메시지를 찾아볼 수 있다.</li>
</ul>
<h3 id="chrome-network-tab">Chrome Network Tab</h3>
<ul>
<li><input disabled="" type="checkbox"> Chrome Network Tab 사용 방법을 익히고 사용할 수 있다.<br>
`curl`로 네트워크 통신을 자세히 보기 위해 터미널에 다음과 같이 입력한다.
![curl명령문으로 요청](https://images.velog.io/images/s_yeah/post/23575ff3-fd56-4a1a-aaf4-9728b176b12e/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.33.52.png)
요청을 하고 나면 다음과 같은 응답을 받을 수 있다. 
![response 해부](https://images.velog.io/images/s_yeah/post/44c27bbb-ff91-4fd1-ad42-ce8635d74e00/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.34.43.png)
응답받은 body를 살펴보면 파일을 읽어오는 순서를 알 수 있는데 실제로 이대로 실행이 되는지는 chrome network tab에서 확인이 가능하다.

</li>
</ul>
<p><img src="https://images.velog.io/images/s_yeah/post/8b1ee656-f1d9-4fe6-a10a-c02f866779e7/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-19%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.52.56.png" alt=""></p>
<p>chrome network tab을 보면 html 파일을 먼저 읽어오고 그 다음 순서로 css, js 파일을 순차적으로 불러오는 것을 확인할 수 있다. 이처럼 크롬 네트워크 탭에서는 어떤 순서로 파일을 읽어오는지를 확인해볼 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 비동기  Promise, Async-await]]></title>
            <link>https://velog.io/@s_yeah/JS-%EB%B9%84%EB%8F%99%EA%B8%B0-Promise-Async-await</link>
            <guid>https://velog.io/@s_yeah/JS-%EB%B9%84%EB%8F%99%EA%B8%B0-Promise-Async-await</guid>
            <pubDate>Tue, 23 Nov 2021 10:19:21 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트는 비동기를 실행하는 특징을 가지기 때문에 다른 언어에 비해 비동기 처리가 훨씬 수월하다. </p>
<h2 id="promise">Promise</h2>
<p>시작과 끝이 명확하지 않기 때문에, 작업 상태를 저장할 클래스를 새로 만들어 담아둔다. 변수를 따로 만들어 이름을 붙이고 만들기 때문에 상태관리에 더 용이하다. </p>
<p>callback으로 작성하면 상태를 명확하게 확인하기 어렵다(<code>return</code>되는 값이 따로 없다 - 함수만 호출함). 따라서 Promise로 추상화하여 간편하게 작성한다.</p>
<p><strong>Promise의 상태 3가지</strong></p>
<ul>
<li>pending</li>
<li>fufilled</li>
<li>rejected</li>
</ul>
<p>Promise의 인자에는 &#39;Promise 실행함수&#39;가 온다. 실행함수 안의 첫번째 인자는 <code>resolve</code>함수와 <code>reject</code>함수가 들어간다.</p>
<pre><code class="language-javascript">const newPromise = new Promise( //promise 실행함수
  (resolve,reject) =&gt; { 
    //어떤 행동을 했을때(fulfilled) -&gt; resolve(&#39;성공한 결과값&#39;);
    //어떤 행동을 했을 때(rejected) -&gt; reject(&#39;실패시 에러객체&#39;);
    //아무것도 안하거나 아직 resolve가 호출되지 않은 경우 -&gt; pending 상태
  }
 )</code></pre>
<p>서비스를 잘 제공하기 위해서는 성공했을 때와 실패했을 때 모두를 컨트롤할 수 있어야 한다. 성공과 실패에 따라 해결방법이 달라야 하기 때문에 로직을 분리되어야 한다.</p>
<p>Promise를 사용하기 위해서는 <code>.then</code>을 사용해야 해당 promise값을 사용 가능하다. </p>
<pre><code class="language-javascript">const theOtherPromise = newPromise.then(); 
//이렇게 써도 theOtherPromise에는 Promise가 전달된다.
console.log(theOtherPromise + &#39;!&#39;) 
//변수로 따로 담았다고 하더라도 내가 원하는 promise안의 값을 사용할 수 없다.
</code></pre>
<p><code>promise.then()</code>을 사용할 경우 원하는 연산을 할 수 있다.</p>
<pre><code class="language-javascript">const anotherPromise = newPromise.then((result)=&gt;{    
  //.then()을 사용해야 그 값을 온전히 전달받아 사용할 수 있다
  console.log(result + &#39;!&#39;);
  return result+&#39;!&#39;; 
  //promise안의 resolve시 값을 그대로 사용하고 싶으면 return문을 이용해
  //.then()문 안에서 원하는 작업을 실행하여 return 해준다.
    })</code></pre>
<p>하지만 Promise 바깥에 어떤 변수를 만들고 이를 <code>return</code>하고자 한다면 크게 문제는 없을지라도 promise를 쓰는 장점을 살릴 수 없다. 상태 변화에 따라 값이 변하고 다르게 처리하기 위해서 promise를 쓰는데 아래의 코드와 같이 쓸 경우 상태가 반영되지 않는다. callback함수와 크게 다르지 않아 <code>promise.then()</code>이 가져다주는 상태를 반영해주지 않는다.</p>
<pre><code class="language-javascript">let temp;
const anotherPromise = newPromise.then((result)=&gt;{    
  //.then()을 사용해야 그 값을 온전히 전달받아 사용할 수 있다
  console.log(result + &#39;!&#39;);
  temp = result+&#39;!&#39;; 
  //이렇게 사용할 경우 callback과 다를바가 없어진다
    })</code></pre>
<p>3번 실습문제(basicChaining) - 동기적으로 작용하게끔 만든다. 작업을 끝내야 그 다음 <code>.then()</code>으로 넘어가기 때문에.</p>
<blockquote>
<p>만약 <code>arr</code>를 따로 선언해 그 안에 결과값을 담아 <code>return</code>한다면?</p>
</blockquote>
<pre><code class="language-javascript">let arr = [];
return somePromise(user1path)
.then((user1text)=&gt;{
  //...
      })
.then((user2text)=&gt;{
  arr.push(user1);
  arr.push(user2);
  return arr;
    })</code></pre>
<blockquote>
<p>이렇게 arr를 따로 선언해 <code>arr.push</code>를 쓰면 안되는 이유
: <code>arr.push</code>를 쓰면 상태와 상관 없이 무조건 user1과 user2를 <code>arr.push</code>해버린다. 데이터가 적을 때는 순서대로 넣겠지만 그렇지 않을 경우에는 장담할 수 없다. 즉 <code>promise</code>를 사용해 비동기로 사용하게되면 순서가 섞여서 들어올수도 있어서 안정적인가?에 대해서는 장담을 못한다(side effect 주의)
<code>.then()</code>을 사용해 첫번째 user1 작업이 실행되고 user2 작업이 실행되도록 상태를 순서대로 조절한다.
<code>return</code>을 <code>promise</code>로 돌려주는 이유도 해당 <code>promise</code>를 해결하고 난 다음에 작업을 실행하도록 안정적으로 운영하기 위해서이다.</p>
</blockquote>
<p>4번 실습문제(promise.all) - 비동기적으로 작동/ 반복처리는 반복문 사용
 처리해야할 연산이 많을 경우에는 비동기적으로 작동하여 처리하는게 속도가 더 빠르다. 
 배열로 .then()에 넘겨준다면 map() 함수로 처리해줄 수 있다. </p>
</br>

<p>5번 실습문제(async-await)</p>
<p>async-await은 비동기로 처리되는 자바스크립트에서 동기적으로 작동하게끔 만든다. </p>
<p>resolve를 다 해서 작업을 받아오기 때문에 동기적으로 사용하는 것과 비슷하다. await을 쓰는건 일부로 동기적인 작업을 하기 위함이다. 따라서 남용해서 작성하지 않도록 동기적 작업이 필요한 경우에만 사용한다. 비동기적으로 사용할 때는 순서가 지켜지지 않고 작업이 이루어지기 때문에 순서를 지켜서 작업을 해야할 때만 이를 사용하도록 한다.</p>
<p>await를 쓰지 않은 문장의 경우에는 await 작업이 다 끝날 때까지 기다리지 않고 비동기적으로 실행이 되기 때문에 실행되는 순서를 조정하기 위해서는 await를 사용해 동기적으로 조절해줘야 한다.</p>
<h3 id="promiseall">Promise.all</h3>
<p>멀티스레드와 같은 작동을 하는 <code>Promise.all</code>은 작업을 더 빠르게 만든다.
promise들의 반복문으로 반복적으로 써야할 작업을 한번에 처리가능하게 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 알고리즘] 단계별 문제풀이 Lv.1 입출력과 사칙연산 ]]></title>
            <link>https://velog.io/@s_yeah/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8B%A8%EA%B3%84%EB%B3%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Lv.1-%EC%9E%85%EC%B6%9C%EB%A0%A5%EA%B3%BC-%EC%82%AC%EC%B9%99%EC%97%B0%EC%82%B0-1</link>
            <guid>https://velog.io/@s_yeah/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8B%A8%EA%B3%84%EB%B3%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-Lv.1-%EC%9E%85%EC%B6%9C%EB%A0%A5%EA%B3%BC-%EC%82%AC%EC%B9%99%EC%97%B0%EC%82%B0-1</guid>
            <pubDate>Thu, 18 Nov 2021 12:22:58 GMT</pubDate>
            <description><![CDATA[<p>JAVA를 안쓴지 오래되어 알고리즘 공부할 겸 백준문제 단계별 문제풀이로 연습한다</p>
<p>문제 &gt; 단계별로 풀어보기 &gt; 입출력과 사칙연산부터 시작!</p>
<h2 id="입출력과-사칙연산">입출력과 사칙연산</h2>
<h3 id="2557번">2557번</h3>
<p><strong>Hello World! 출력하기</strong>
<img src="https://images.velog.io/images/s_yeah/post/d5956c19-4fad-4024-8f4f-9f66ddbb719a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.44.53.png" alt="백준알고리즘 2557번"></p>
<p>가장 기본이고 코딩의 시작이 아닐까 하는 Hello World! 출력하기
가장 쉬운 문제라고 생각했는데 계속 컴파일 오류나서 당황;;</p>
<p>우선 java 가장 기본적으로 작성해야하는 부분을 작성해주지 않으면 컴파일 에러가 난다.</p>
<pre><code class="language-java">public class printHelloWorld{
    public static void main(String[] args){
    //내용 작성
    }
}</code></pre>
<p>그것도 모르고 몇번 해보다가 구글 검색으로 결국 해결했다. 반드시 클래스명도 <code>public class Main</code>으로 해줘야한다는 것도 몇번의 컴파일 에러와 검색 끝에 알았다. 어쩐지 문제의 난이도에 비해 정답률이 너무 낮더라니... </p>
<p>그렇게 해서 얻어낸 정답은, 다음과 같다. 
<img src="https://images.velog.io/images/s_yeah/post/5c66e63a-2a25-472f-b125-52a8b6ba4191/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.46.17.png" alt="백준알고리즘 2557번 코드"></p>
<p>(느낌표, 클래스명 등 꼭 필요한건 빼먹지 말고 다 써야한다, 안그러면 계속 컴파일 에러난다ㅠ)</p>
<h3 id="10171번">10171번</h3>
<p><strong>고양이를 출력하기!</strong></p>
<p><img src="https://images.velog.io/images/s_yeah/post/eb1229be-0819-44d4-8bb4-db0052e356ee/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.01.45.png" alt="백준알고리즘 10171번"></p>
<p>예제 출력에 있는 고양이를 우선 복사 후 <code>System.out.println(&#39;&#39;)</code>에 한줄씩 붙여 넣는다.</p>
<p>escape character를 어떻게 출력하는지를 묻는 문제로 escape문자를 사용해 예제 출력처럼 온전한 고양이를 출력해야한다.</p>
<table>
<thead>
<tr>
<th align="left">Escape Characters</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">\t</td>
<td align="left"><strong>tab</strong>을 넣기 위함</td>
</tr>
<tr>
<td align="left">&#39;</td>
<td align="left"><strong>&#39;</strong> (작은따옴표)를 넣기 위함</td>
</tr>
<tr>
<td align="left">&quot;</td>
<td align="left"><strong>&quot;</strong> (큰 따옴표)를 넣기 위함</td>
</tr>
<tr>
<td align="left">\r</td>
<td align="left"><strong>캐리지 리턴</strong>(해당 시점부터 줄바꿈)</td>
</tr>
<tr>
<td align="left">\\</td>
<td align="left"><strong>\</strong> (백슬래시)를 넣기 위함</td>
</tr>
<tr>
<td align="left">\n</td>
<td align="left"><strong>다음줄</strong>로 바꾸기(한줄 삽입)</td>
</tr>
<tr>
<td align="left">\f</td>
<td align="left"><strong>폼 피드</strong>를 넣기 위함</td>
</tr>
<tr>
<td align="left">\b</td>
<td align="left"><strong>백스페이스</strong></td>
</tr>
</tbody></table>
<p>위의 탈출 시퀀스를 적용하고 나면 예제대로 출력 가능하다. </p>
<p><img src="https://images.velog.io/images/s_yeah/post/725c9ff6-d389-41cc-8c08-0083353c2b01/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.39.29.png" alt="백준알고리즘 10171 코드">
빈칸이 정확히 예제와 똑같은지 주의하도록 하자. 간단해 보이는 문제도 6번이나 틀렸기 때문에,,ㅎㅎ빈칸하나하나 찍어가면서 고치는 것보다 예제 출력을 한줄씩 그대로 가져오는게 제일 정석이다..</p>
<h3 id="1000번-">1000번 ~</h3>
<p><strong>숫자 입력받고 사칙연산 결과 출력하기</strong>
<img src="https://images.velog.io/images/s_yeah/post/9b2ac1bc-ad10-4fd9-8a82-b0aa1bbed348/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.02.56.png" alt="백준알고리즘 1000번"></p>
<p>이번 문제는 입력값을 받아 그 값으로 원하는 작업을 수행한 뒤 출력하는 문제다.
지금까지 문제풀이들과 다른점은 사용자로부터 &#39;입력&#39;받아야 한다는 것!</p>
<p>입력받은 값을 사용하려면 우선 입력을 받는데 필요한 java pkg를 import 해준다.</p>
<pre><code class="language-java">import java.util.* 
//이렇게 쓰면 굳이 입력에 필요한 클래스뿐만 아니라 package내 다른 기능도 포함되어 있다.
import java.util.Scanner
//이럴 경우 입력받을 때 필요한 scanner 클래스만 사용가능하다</code></pre>
<p>import 받은 Scanner 클래스 객체를 생성하여 사용자가 입력한 값을 받아오는 기능을 수행하도록 한다. </p>
<pre><code class="language-java">Scanner sc = new Scanner(System.in);
//System.in은 화면상에서 들어온 입력을 받음</code></pre>
<p>생성한 클래스 객체로 scanner 객체를 사용한다.</p>
<pre><code class="language-java">int num1 = sc.nextInt();    //입력받을 값이 int형일 때 nextInt();
int num2 = sc.nextInt();    //그 다음 int 값을 num2에 저장하겠다는 메서드</code></pre>
<p>위 순서대로 작성하고 나면 더하기, 빼기, 곱하기 모두 같은 방식으로 결과를 낼 수 있다. 더하기한 코드는 아래와 같다. 
<img src="https://images.velog.io/images/s_yeah/post/b794c620-4195-4637-a711-779824ae6949/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-18%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.24.06.png" alt="백준알고리즘 1000 코드"></p>
<p>나누기 같은 경우에는 자료형을 <code>double</code>로 해야 나눗셈할 때 소수점자리까지 계산할 수 있다. <code>int</code>일 때는 정수 계산이므로 소수점으로 나눠지는 값을 구하는 나누기에 적절하지 못하다.</p>
<p><img src="https://images.velog.io/images/s_yeah/post/858cfd8f-247f-4cdd-b1fd-fa8cd51fd7e3/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-18%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.34.13.png" alt="백준알고리즘 1008번 코드"></p>
<p><img src="https://images.velog.io/images/s_yeah/post/be5db75c-68ec-441b-8736-d3d9c23fd8f6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-18%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.48.19.png" alt="백준알고리즘 10430번 코드">
괄호를 씌워야지만 연산의 우선순위가 지켜지는 줄 알고 문제에서 나온 그대로 괄호를 쳐서 작성했는데 모듈러 연산(%)이 더하기나 곱셈보다 더 우선순위가 높아 굳이 괄호로 안 묶어도 같은 결과가 나온다. 대신 곱셈과 나눗셈은 우선순위가 같아 주의해야한다.
모듈러의 특징으로 1,2번 식과 3,4번 식은 어떤 입력값을 넣어도 1,2번과 3,4번의 결과값은 서로 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자료구조 - Stack / Queue]]></title>
            <link>https://velog.io/@s_yeah/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@s_yeah/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Tue, 16 Nov 2021 09:00:39 GMT</pubDate>
            <description><![CDATA[<h1 id="자료구조란">자료구조란?</h1>
<p>여러 데이터의 묶음을 저장하고, 사용하는 방법을 정의하는 것.
다양한 데이터를 유용하게 사용하기 위해서는 데이터를 분석하고 활용할 수 있어야 한다. 또한 데이터를 사용하려는 목적에 맞게 분류하고 구분지어야 필요한 곳에 적절히 활용 가능하다.
스택,트리,큐 등과 같은 방식으로 데이터를 효율적으로 다루는 자료구조가 사용된다.
많은 자료구조를 익혀둘수록 문제해결에 적합한 해결방안을 찾기 수월해진다.</p>
<p>자료구조가 가지는 특징
자료구조를 사용하기 적합한 상황
자료구조의 작동원리 - 직접 구현
다른 자료구조와의 차이점</p>
<h2 id="stack">Stack</h2>
<p>데이터를 순서대로 쌓는 선형(Linear)자료구조로 가장 나중에 들어온 데이터가 가장 먼저 나갈 수 있는(Last In First Out(LIFO)/First In Last Out(FILO)) 방식으로 데이터의 입출력이 한 방향으로만 흐른다.</p>
<h4 id="직접-구현해보기">직접 구현해보기</h4>
<p>뒤로가기, 앞으로 가기 동작</p>
<p>수도코드로 표현을 해본다면,</p>
<pre><code class="language-javascript">function browser(동작, 시작페이지){
  let 현재페이지, 다음페이지 스택, 이전페이지 스택
  if(새로운 페이지 접속시){
    현재페이지를 이전페이지 스택에 보관,
    새로운 페이지를 현재페이지로 저장
      }
  if(뒤로가기){
    현재페이지를 다음페이지 스택에 보관,
    이전페이지 스택에 있던 페이지를 불러와 현재페이지로 저장
     }
  if(앞으로 가기){
    현재페이지를 이전페이지 스택에 보관,
    다음페이지 스택 중 가장 위에 있던 페이지를 불러와 현재페이지로 저장
     }

}
</code></pre>
<p>실제 코드로 구현을 해보게 된다면 다음과 같다.</p>
<pre><code class="language-javascript">// actions = [A,-1,C,D,1,1,-1] , start = &#39;E&#39;
//1은 앞으로 가기, -1은 뒤로 가기, 문자열은 접속한 페이지
function browser(actions, start) {
  let prev = [];    //뒤로가기에 필요한 stack
  let next = [];    //앞으로가기에 필요한 stack
  let prevTop = 0; 
  let nextTop = 0;
  let nowPage = start;

for(let i=0;i&lt;actions.length;i++){
  //새로운 페이지로 접속할 경우 prev 스택에 원래 있던 페이지를 넣고 next 스택을 비웁니다.
  if(typeof(actions[i]) === &#39;string&#39;){
    prev.push(nowPage);
    prevTop++;
    nowPage = actions[i];
    nextTop = 0;
    next = [];
  }
  // 뒤로 가기 버튼을 누를 경우 원래 있던 페이지를 next 스택에 넣고 
  //prev 스택의 top에 있는 페이지로 이동한 뒤 
  //prev 스택의 값을 pop 
  if(actions[i] === -1 &amp;&amp; prevTop &gt; 0){
    next.push(nowPage);
    nextTop++;
    prevTop--;
    nowPage = prev[prevTop];
    prev.pop();
  }
  //앞으로 가기 버튼을 누를 경우 원래 있던 페이지를 prev 스택에 넣고 
  //next 스택의 top에 있는 페이지로 이동한 뒤 
  //next 스택의 값을 pop
  if(actions[i] === 1 &amp;&amp; nextTop &gt; 0){
    prev.push(nowPage);
    prevTop++;
    nextTop--;
    nowPage = next[nextTop];
    next.pop();
  }
  //브라우저에서 뒤로 가기, 앞으로 가기 버튼이 비활성화일 경우(클릭이 되지 않을 경우)에는 스택에 push 하지 않음 (prevTop, nextTop이 0보다 작을 경우 스택에서 꺼내올 수 없음)

}
return [prev, nowPage, next]
}</code></pre>
<h2 id="queue">Queue</h2>
<p>데이터를 순서대로 쌓고 빼내는 선형(Linear)자료구조로 선입선출(First In First Out(FIFO)) 방식으로 데이터가 입력된 순서대로 처리할 필요가 있을 때 사용된다. 주로 데이터들을 대기시켜야 할 때 queue 사용한다.</p>
<p>Insert - Enqueue
Remove - Dequeue
Front 
Rear</p>
<p>+) 원형으로 된 queue</p>
<h4 id="직접-구현해보기-1">직접 구현해보기</h4>
<p>프린터기에서 문서 출력</p>
<p>컴퓨터에서 출력 명령 -&gt; 큐에 하나씩 쌓임 -&gt; 큐에 들어온 문서를 가장 먼저들어온 문서부터 출력</p>
]]></description>
        </item>
    </channel>
</rss>