볼륨연결

볼륨연결은 폴더를 연결한다고 생각하면 된다.(링크)

호스트의 폴더를 컨테이너의 디렉토리에 마운트해서 호스트에서 해당 폴더에 파일을 생성,삭제,수정을 하면 컨테이너에 바로 적용되도록 할 수 있다.

 

보통 이미지에 들어있는 os들은 최소설치이기 때문에 vi, sudo 같은 명령어가 안되는 경우가 많아서 호스트에서 직접 파일을 수정해서 넣어주면 더 편리한 경우가 많다!

 

 

실습 (목표 : 기본적으로 제공되는 index.html 수정)

1. 아파치 컨테이너 실행 (볼륨 연결X)

docker run -d httpd
docker exec -it [CONTAINER ID] bash

index.html 파일은 /usr/local/apache2/htdocs 에 위치해있다. 해당 파일을 수정하면 웹서버(아파치)에 접근했을 때 나오는 기본 페이지를 변경할 수 있다.

vi나 sudo 같은 명령어가 없다고 나온다.

 

 

2. 볼륨을 연결한 아파치 컨테이너 실행 (볼륨 연결O)

//볼륨에 연결하기 예제
docker run -d -v [HOST_PATH]:[CONTAINER_PATH] [IMAGE]
//볼륨 연결 실습
docker run -d -p 9097:80 -v /home/docker_mount/lecture:/usr/local/apache2/htdocs --name=myHttpd httpd

-v(volume)옵션을 통해 HOST ↔ CONATINER 간 디렉토리를 손쉽게 마운트 할 수 있다.

 

해당 상태에서 웹서버에 접근하면 HOST디렉토리에는 아무것도 없으므로 아래와 같은 화면이 나온다. (기본적으로 생성되는 index.html은  It works! 문구가 표시되도록 되어있다.)

이제 호스트OS에서 설정해둔 디렉토리 (/home/docker_mount/lecture)에 가서 index.html을 생성해보자.

cd [볼륨마운트한 경로]
vi index.html

호스트OS에서 생성한 index.html

 

이렇게 파일을 생성한 후 아파치 서버에 접속해보면 아래와 같이 잘나오는 것을 확인할 수 있다.

 

 

정리 : 호스트OS와 컨테이너간 디렉토리 마운트를 위해서는 볼륨을 이용하자. (docker run -v [HOST]:[CONTAINER])

attach로 접근할 수 없는 경우

OS만 있는 컨테이너와 다르게 다른 command가 있는 컨테이너들은 단순히 attach로 리눅스 쉘에 접근할 수가 없다.

docker run -d -p 8080:80 httpd

이렇게 컨테이너를 실행시킨 후 

docker attach [방금실행시킨 httpd ID]

attach 명령어로 리눅스 쉘에 접근하려고 하면 접근이 안된다.

run할 때 -it를 붙이지도 않았을 뿐더러 실행되고 있는 명령어가 /bin/bash가 아니라 httpd-foreground 이기 때문.

 

그렇다면 실행할 때 -it 옵션을 준다면?

docker run -dit httpd
docker run -dit httpd bash

 

이렇게 실행해서 docker attach를 하면 어떨까? 컨테이너가 바로 종료되어버린다.

두번째 줄에있는 bash명령어를 줘도 마찬가지.

 

다른 명령어가 실행되고 있을 때 터미널에 접근하고 싶다면?

docker exec -it [CONTAINER_ID] bash

exec -it 명령어로 해결 할 수 있다. 상호작용할 수 있는 bash를 실행시켜준다.

옵션인 -it의 의미는 이전 강의에와 마찬가지인것 같다.

-i : interactive 상호작용

-t : terminal 모드 /bin/bash로 상호작용한다.

 

 

 

 


정리
1. os(ubuntu) 이미지일 때 터미널 실행 법
docker run -dit ubuntu
docker attach [CONTAINER_ID]

2. while process (httpd) 이미지 일 때
docker run -d -p 8080:80 httpd (docker run -dit를 해도 상관없음)
docker exec -it [CONTAINER_ID] bash

도커는 이미지 실행 시에 command를 지정하게 되어있는데 아파치같은 것은 아파치 실행 명령어가 command여서 attach로 접근하면 bash 접근불가.
이럴 때는 docker exec -it [CONTAINER_ID] bash 로 상호작용할 수 있도록 해주면 된다.

디스패치 / 액션 / 리듀서

useReducer를 사용하면 state를 변경해야할 때 dispatch함수를 호출하고, 액션 객체를 전달해서 state를 변경한다.

  • dispatch : 액션을 실행해주는 함수.
  • 액션 객체 : dispatch에 전달되는 객체로 리듀서가 실행해야할 로직을 알려주는 action.type과 리듀서가 변경해야할 state값을 가지고 있다.
  • reducer : 액션 객체를 해석해서 state를 직접 바꿔주는 함수. (리듀서는 dispatch가 실행될 때 마다 자동으로 호출된다.) --> reducer함수에서 반환하는 값은 새로운 state이다.

useReducer를 사용하면 state 변경 순서는 아래와 같아진다.

  1. 이벤트 발생
  2. dispatch 호출 (이벤트 객체를 만들어서 dispatch 함수 호출 시 이벤트 객체를 넘겨준다.)
  3. dispatch가 호출되면 자동으로 리듀서가 호출되면서 액션 객체를 해석하여 특정 로직 실행 (state변경)

 

reducer함수에서 주의해야할 점은 불변성을 꼭 지켜야한다는 것!

reducer함수에서 반환하는 값은 새로운 state인데 항상 새로운 객체를 만들어 바뀐 값만 바꿔줘야한다. 이를 위해 spread연산자를 활용해서 불변성을 지키자!

 

ACTION TYPE

  • 액션 타입은 리듀서함수에서 어떤 state를 수정할 로직을 실행해야할지 결정하는 중요한 정보이다.
  • 액션 타입은 상수로 따로 빼두는 것이좋다.
  • 액션 타입은 대문자로 하는 것이 커뮤니티 규칙이다.

 

 

TicTacToe.jsx

import React, {useCallback, useReducer, useState} from "react";
import Table from "./Table";

//state 초기값
const initialState = {
    winner : '',
    turn: 'O',
    tableData:[['','',''],['','',''],['','','']],
}
//액션 타입은 상수로 따로 빠두는것이 좋다.
//액션의 이름은 대문자로 하는것이 커뮤니티 규칙!
const SET_WINNER = 'SET_WINNER';

//리듀서 == 함수
const reducer = (state, action) => {
    //리듀서는 액션을 디스패치 할 때마다 실행된다.
    switch(action.type){
        case SET_WINNER:
            //state.winner = action.winner; 이렇게 하면 안된다!!!!
            //우리는 항상 새로운 객체를 만들어서 바뀐 값만 바꿔줘야한다. 불변성 지켜!.
            return {
                ...state,
                winner: action.winner,
            }

    }
}

const TicTacToe = () => {
    /*
    const [winner, setWinner] = useState('');
    const [turn, setTurn] = useState('O');
    const [tableData, setTableData] = useState([['','',''],['','',''],['','','']]);
    */
    const [state, dispatch] = useReducer(reducer, initialState);

    //컴포넌트에 있는 함수들은 다 useCallback으로 감싸주자.
    const onClickTable = useCallback(()=>{
        //dispatch는 액션을 실행해주는 함수이다. dispatch에 전달되는 값은 액션객체이다.
        //액션 객체를 해석해서 state를 직접 바꿔주는 역할을 하는 것이 리듀서이다.
        dispatch({type:SET_WINNER, winner:'O'});
    },[]);

    return(
        <>
            <Table onClick={onClickTable} tableData={state.tableData}/>
            {
                state.winner && <div>{state.winner}님의 승리</div>
            }
        </>
    );
}

export default TicTacToe;

 

Table.jsx

import React from 'react';
import Tr from "./Tr";

const Table = ({onClick, tableData}) => {
    return (
        <table onClick={onClick}>
            {Array(tableData.length).fill().map((tr,i)=><Tr rowData={tableData[i]}/>)}
        </table>
    );
};

export default Table;

 

Tr.jsx

import React from 'react';
import Td from "./Td";

const Tr = ({rowData}) => {
    return (
        <tr>
            {
                Array(rowData.length).fill().map((td)=><Td/>)
            }
        </tr>
    );
};

export default Tr;

 

+ Recent posts