반응형

 

갑자기 부캠 프로젝트에서 시간 문제로 미완성 했었던

drag and drop 크기 변경 기능을 한번 구현해보고 싶어서 잠깐 해보았다.

 

레이아웃 틀만 잡아주고 팀원에게 넘기고 다른곳으로 넘어간곳이라 새로운 기능 추가가 어떤 사이드 이펙트를 낳을지 몰라 무섭긴 하지만... 프로젝트도 종료되었고 drag and drop 연습겸 해보았다!

 

기본 원리

const PAGEVW = 100;
const OPENSIZE = 82;
const SIDEBARSIZE = 18;
const CLOSEDSIZE = 61;

export const sizestate = atom<number>({
  key: 'sizeState',
  default: OPENSIZE,
});

구현할때 왼쪽부터 차례대로 Channels, Workspace(chat), Thread(reply)라고 나눴었고.

 

전체 width를 100(%)이라고 했을때

댓글창이 안열려 있다면 workspace의 width가 82(%)이고 channel이 18(%),

열려있다면 workspace의 width가 61(%)이고 나머지가 reply(21%) 창과 channel이 18(%)이 되도록 구현해놓은 상태였다.

 

그리고 위에서 구한 값을 recoil에 넣어 전역으로 관리했고, styled-component에 props로 넣어 각 화면의 width를 비율대로 맞추게 했다.

 

// workspace 길이 
export const mainWorkspaceSizeState = selector<number>({
  key: 'mainWorkspaceSizeState',
  get: ({ get }) => {
    const { isOpened } = get(replyToggleState);
    const OPENEDSIZE = get(sizestate);

    if (isOpened) return CLOSEDSIZE;
    return OPENEDSIZE;
  },
});

//reply 길이
export const replyWorkspaceState = selector<number>({
  key: 'replyWorkspaceSizeState',
  get: ({ get }) => {
    const { isOpened } = get(replyToggleState);

    if (!isOpened) return 0;

    const size = PAGEVW - get(mainWorkspaceSizeState) - SIDEBARSIZE;
    return size;
  },
});

(위 코드는 workspace 의 width를 기준으로 reply 창이 열렸을때, 안열렸을때 recoil의 selector를 이용해 각각 길이를 계산해주는 코드이다.)

 

그렇다면 여기서 넣어줄 기능은 drag and drop으로 동적으로 width를 변경할 수 있는 코드만 넣으면 되는데...

 

다음 방식을 사용했다.

 

저 빨간 선(div)를 구현한 코드는 다음과 같다.

 

import React from 'react';
import { useSetRecoilState } from 'recoil';
import { widthSizeState } from '@state/workspace';

import Container from './styles';

const DraggableDiv = (): JSX.Element => {
  const setWidthSize = useSetRecoilState(widthSizeState);

  const dragEventHandler = (e: React.DragEvent) => {
    const { target } = e;
    const { parentNode } = target;

    const parentBox = parentNode.getBoundingClientRect();
    setWidthSize(Math.max(47, (e.clientX / parentBox.width) * 100 + 1));
  };

  return <Container draggable="true" onDragEnd={dragEventHandler} />;
};

export default DraggableDiv;

원리는 상당히 간단하다.

 

drag 하고 마우스를 놨을때 drag가 끝난 점의 좌표를 계산해 넣어준다.

클릭한 점의 X 좌표를 빨간선 div의 부모 width로 나누고 100을 곱하면 백분위가 되는데

왜냐하면 전체 박스 길이(width)에서 맨 왼쪽을 기준으로 오른쪽만큼 간게 e.clientX가 되므로

단순히 나눠주고 100을 곱하면 백분위가 된다.

 

참고) draggable 이 달려있는게 빨간선이다.

 

그렇게 구한 값을 recoil에 적용해주면 workspace의 width이고, selector를 통해 reply의 크기가 자동으로 반영되므로 

화면의 크기를 동적으로 바꿀 수 있게된다.

 

 

channel도 동일하게 drag and drop을 붙일 수 있는데 노가다의 문제이므로 

drag and drop을 써본거에 만족하고 끝내려 한다. 

 

(구현하려면 DraggableDiv에서 setRecoilValue만 바꿔주면 되는데 위에서 주입하거나 view와 logic을 분리해서 구현하면 될꺼같다?)

반응형

'Front-end > React' 카테고리의 다른 글

react 17(개인 공부용)  (1) 2022.06.09
React18(개인 공부 메모용)  (0) 2022.06.09
react TMI  (0) 2022.04.13
redux, redux-saga로 로딩, 에러 페이지 구현  (0) 2022.02.12
redux 기본 구조  (0) 2022.02.02
반응형

책임 연쇄 패턴(Chain of Responsibility)

 

클라이언트의 요청을 수신해서 처리할 수 있는 객체를 찾을때까지 집합 내부에서 요청을 전달하는 

패턴

 

'파이프 라인'이라고 비유하면 이해가 쉽습니다. 

파이프라인을 따라 가다가 요청을 처리 가능한 그 handler가 이벤트를 처리하고,

처리불가능하면 다음 handler에 요청을 넘깁니다.

 

장점)

 

결합도를 낮추고 요청의 발신자, 수신자 분리

클라이언트는 내부를 알 필요가 없다

집합 내 순서를 유연하게 처리 가능

새로운 요청을 처리하기 매우 쉬워진다(계속 붙여주면 됨) 

 

단점)

디버깅이 쉽지 않다

사이클이 발생하지 않게 주의

 

코드 예시

 

숫자를 넣었을때 10, 100, 1000이하인지 처리하는 handler를 만들어봅시다.

 

class COR {
    
    constructor(){
        this.setLog();
    }

    setLog(){
        this.log = 0;
    }

    setNextChain(nextChain){
        this.chain = nextChain;
        return nextChain;
    }

    support(number){
        if(this.resolve(number)){
            this.done(number);
        }
        else if(this.chain){
            this.chain.support(number);
        }
        else{
            this.fail(number);
        }
    }

    resolve(number){
        return false;
    }

    done(number){
        console.log(`${number} less than ${this.log} `);
    }

    fail(number){
        console.log(`${number} bigger than ${this.log} `);
    }
}

기본 interface는 위의 코드와 같습니다.

 

support 라는 메소드로 이벤트를 처리하는데,

지금 이벤트 핸들링이 가능하면 this.done 메소드를 호출해서 이벤트 체인을 끝내고 자신의 이벤트를 실행합니다.

그렇지 않으면 체인을 걸어준 다음 handler로 넘기고,

끝까지 도착했으면 이벤트 핸들링이 불가능한 뜻이므로 this.fail 메소드를 실행해 체이닝을 종료합니다.

 

class TenChain extends COR{
    setLog(){
        this.log = 10;
    }


    resolve(number){
        if(number<10){
            return true;
        }
        return false;
    }
}

class HundredChain extends COR{
    setLog(){
        this.log = 100;
    }

    resolve(number){
        if(number<100){
            return true;
        }
        return false;
    }
}

class ThousandChain extends COR{
    setLog(){
        this.log = 1000;
    }
    resolve(number){
        if(number<1000){
            return true;
        }
        return false;
    }
}

각각 10, 100, 1000 이하인지 확인하는 간단한 예시 이벤트 핸들러입니다. 

실제 책임 연쇄 패턴 사용 예시는 이보다 복잡하지만 전체 구조를 훑어보기에 알맞은 예시인거 같아서 작성했습니다.

 

let PC = new TenChain();
let cursor = PC;

cursor = cursor.setNextChain(new HundredChain());
cursor = cursor.setNextChain(new ThousandChain());

PC.support(1);
PC.support(30);
PC.support(210);
PC.support(220);
PC.support(3100);

/*
1 less than 10 
30 less than 100 
210 less than 1000 
220 less than 1000 
3100 bigger than 1000 
*/

10 handler -> 100 handler -> 1000 handler 로 체인이 걸려있고, 실제 테스트해보니

잘 작동하는것을 볼 수 있습니다.

 

 

 

 

ref

 

https://medium.com/geekculture/understanding-the-chain-of-responsibility-pattern-d729ef84621c

 

Understanding The Chain of Responsibility Pattern

Chain of responsibility pattern is a behavioral design pattern that we use to achieve loose coupling in our software design, where a…

medium.com

 

반응형

'소프트웨어공학 > 디자인패턴' 카테고리의 다른 글

메모  (0) 2022.03.25
프론트엔드에서의 레포지토리 패턴?  (0) 2022.03.18
[행위] 전략 패턴 & 커맨드 패턴  (0) 2022.03.07
[행동] 중재자 패턴  (1) 2022.03.01
[구조] 데코레이터 패턴  (0) 2022.02.28
반응형

SPA(Single Page Application)이란? 

 

과거에는 웹 페이지를 클릭하면 하나의 문서를 전달하고, <a href="페이지 주소"> 같은 하이퍼 링크를 누르면

새로운 페이지를 서버에서 전송받아 보여주고는 했습니다.

즉, 새로운 페이지를 요청할때마다 정적 리소스가 다운로드 되고 (새로운) 전체 페이지가 전부 다 렌더링 됩니다.

 

SPA와 기존 방식(MPA)

SPA는 기존 방식과 달리 웹 페이지에 필요한 리소스(HTML,CSS,JS)를 한번에 다 다운로드 받은 뒤

새로운 페이지 요청이 있을 경우 페이지 갱신에 필요한 데이터만 서버에서 전달받아 페이지를 갱신하는 방식(AJAX)으로 구현이 가능합니다.

 

즉, 빈 도화지(HTML)에 그림(JS로 동적 렌더링)을 그리는 식으로 작동합니다.


JS로 간단하게 구현해봅시다.

 

폴더 구조

└─src
|   ├─pages
|   |   ├─ RouterButton.js
|   |   ├─ A.js
|   |   ├─ B.js
|   |   └─ C.js
|   ├─BaseComponent.js 
|   └─App.js
└─ index.html

page는 A,B,C 페이지가 있고

요청에 따라 index.html에 동적으로 A,B,C 페이지를 각각 그려줄 것입니다.

하얀 도화지에 그림(렌더링)을 그린다고 보시면 이해가 편합니다.

 

HTML

 

<html>
  <head>
    <title>SPA</title>
  </head>
  <body>
    <main class="app">
    </main>
    <script type="module" src="./src/app.js"></script>
  </body>
</html>

우선 기본 base html입니다. 

우리는 기본 html에 JS로 동적으로 화면을 그려줄 것 입니다.

 

(*type="module"을 쓰면 global scope가 아니고 독립적인 module scope가 생기고, ES6 module(import, export)를 사용 가능합니다.) 

 

간단히 렌더링 되기 이전까지의 과정을 요약해보자면

 

1. www.홈페이지 요청

2. 서버에서 리소스(HTML, JS, CSS)등을 클라이언트에 보내줌

3. HTML,CSS, JS 다운로드 완료

4. JS 동작 

5. HTML에 렌더링

 

BaseComponent.js

 

A,B,C 페이지 javascript폴더에 공통적으로 쓸 부모 클래스를 정의해줍니다.

 

아래에서 쓰이는 예시는 템플릿 메소드 패턴이라고 디자인 패턴을 사용한 것인데

부모 클래스에서 기본적인 로직을 짜고 아래 부분(자식 클래스)에서 구체화하는식으로 동작하게 구현하는 

방식입니다.

 

(예시는 객체지향 패러다임을 썼지만 객체지향적인 컨셉으로 짜도 되고 함수 컨셉으로 짜도 됩니다.)

export default class BaseComponent {

    constructor({$parent, setState, state}){
        this.$parent = $parent;
        this.state = state;
        this.setState = setState;
    }
    
    template(){
        return '';
    }

    #render(){
        const template = this.template();
        this.$parent.insertAdjacentHTML('beforeend', template);
    }

    setEvent(){

    }

    renderSequence(state){
        this.state = state;
        this.#render();
        this.setEvent();
    }
}

constructor에서 주입받는건 3가지인데 

이 중 $parent는 HTML의  <main class="app"></main> 값을 가지고 있는 변수(querySelector)입니다.

setState, state 두가지는 이 후에 차차 알아봅시다.

 

저런식으로 구현한다면 만약 class="app"을 가진 root element가 아니라

다른 element값을 주입해서 그 아래에 렌더링되는식으로 부모클래스가 자식 클래스를 제어할 수 있는 유연성을 가질 수 있습니다.

 

이것을 유식한 말로 DI(Dependency Injection)이라 합니다.

 

상속받은 자식 클래스들 (A.js, B.js, C.js)

 

우리가 자식 클래스에서 구현할것은 

template() 함수 와 setEvent() 함수입니다.

 

template() 함수는 안에 넣을 뼈대(string)를,

setEvent()는 렌더링 이후 페이지에 붙여줄 이벤트만 작성해주면 됩니다. (간단 예시니 event는 붙이지 않았습니다)

 

그럼 부모 클래스가 로직을 알아서 제어해서 렌더링 시켜줄 것입니다.

 

>> 자식 클래스들 코드 

더보기
import BaseComponent from '../BaseComponent.js';

export default class A extends BaseComponent{

    template(){
        return `IM A!
        ${RouterButton()}
        `;
    }

    setEvent(){

    }
}

export default class B extends BaseComponent{


    template(){
        return `IM B!
        ${RouterButton()}
        `;
    }


    setEvent(){
    }
}
export default class B extends BaseComponent{



    template(){
        return `IM B!
        ${RouterButton()}
        `;
    }


    setEvent(){
    }
}

export default function RouterButton(){
    return `
        <button type="button" class="A">
            A
        </button>
        <button type="button" class="B">
            B
        </button>
        <button type="button" class="C">
            C
        </button>
    `
};

 

이제 SpaRouter를 작성해 봅시다.

 

import A from './pages/A.js';
import B from './pages/B.js';
import C from './pages/C.js';

class app{

    constructor(){
        this.state = { 'locate' : window.location.pathname};
        this.root = document.querySelector('.app');
        
        const ObjectForDI = {$parent:this.root, setState : this.setState.bind(this), state : this.state};

        this.A = new A(ObjectForDI);
        this.B = new B(ObjectForDI);
        this.C = new C(ObjectForDI);

        this.render();
        this.setDummyEvent();
    }

    setState (newState){
        const { locate } = this.state;
        this.state = {...this.state, ...newState};
        this.render();
    }
    
    render(){
        this.root.innerHTML = '';

        let { locate } = this.state;

        console.log(locate);

        if(locate === '/A'){
            this.A.renderSequence(this.state);
        } 
        else if(locate === '/B'){
            this.B.renderSequence(this.state);
        }
        else{
            this.C.renderSequence(this.state);
        }

        this.historyRouterPush(locate);
    }

    historyRouterPush(locate) {
        window.history.pushState({}, locate, window.location.origin + locate);
    }
}

new app();

 

코드를 나눠서 살펴 봅시다.

 

라우팅 처리 방식 

 

    render(){
        this.root.innerHTML = '';

        let { locate } = this.state;

        console.log(locate);

        if(locate === '/A'){
            this.A.renderSequence(this.state);
        } 
        else if(locate === '/B'){
            this.B.renderSequence(this.state);
        }
        else{
            this.C.renderSequence(this.state);
        }
    }

지금 페이지의 경로(window.location.pathname) 에 따라 A, B, C 페이지가 라우팅됩니다.

경로가 /A면 A 페이지, /B면 B 페이지, 그외엔 C 페이지가 렌더링 되고 있습니다.

 

 

경로 변경 방식

 

    setState (newState){
        // newState = {locate : 'newPath' }
        this.state = {...this.state, ...newState};
        this.render();
    }

 

 특정 이벤트가 일어나면 setState를 호출하고, 다시 렌더링되게(위에서 보았던 render 함수 호출)됩니다.

 

그리고 state와 setState는 아까 BaseComponent.js의 생성자에서 본 state와 setState의 정체입니다.

부모 클래스(app.js)에서 자식 클래스(BaseCompoent 상속받은 A.js, B.js 등)에게 함수와 공통 객체를 주입해주고,

페이지를 바꿀 (정보 갱신) 타이밍이 오면 setState 함수를 자식 클래스에서 호출해서 해당 페이지를 전부 rerender 하게 됩니다.

 

리엑트에서 상태 변경때 쓰는 방식과 비슷하지 않나요?

내부 구현은 매우 다를거라 예상되지만... 비슷하게 작동합니다.

(이 코드에서 엄밀히 따지면 setState보다는 setRouterPath 등 다른 이름이 어울릴꺼 같긴 해요) 

 

뒤로가기, 앞으로 가기 구현

 

    historyRouterPush(locate) {
        window.history.pushState({}, locate, window.location.origin + locate);
    }

브라우저 API인 히스토리 API를 사용해서 매우 쉽게 구현 가능합니다.

 

이상 간단하게 SPA 구현 방식을 알아봤습니다.

 

 

ref

 

https://devopedia.org/single-page-application

 

Single Page Application

A web application broadly consists of two things: data (content) and control (structure, styling, behaviour). In traditional applications, these are spread across multiple pages of HTML, CSS and JS files. Each page is served in HTML with links to suitable

devopedia.org

 

참고)

 

클릭 이벤트 처리방법

 

    dummyClickEvent = ({target}) => {
        if(target.classList.contains('A')){
            this.setState({...this.state, locate : '/A'});
        }
        if(target.classList.contains('B')){
            this.setState({...this.state, locate: '/B'});
        }
        if(target.classList.contains('C')){
            this.setState({...this.state, locate: '/C'});
        }
    }


    setDummyEvent(){
        this.root.addEventListener('click', this.dummyClickEvent);
    }

 

반응형
반응형
 
연구실 다닐때 resnet 구조를 desnet으로 변경하면 어느정도 성능이 향상되나? 실험해본 보고서
 
결과 : 0.4% 정도 향상
 
(학부생의 분석 결과라 잘못된 내용이 있으면 피드백 주시면 감사하겠습니다)
 

보고서_이충헌.pdf
0.61MB

 

반응형

'인공지능' 카테고리의 다른 글

chat gpt assistant api 써보기 (feat: 예제 코드)  (1) 2024.01.21
반응형

전략 패턴

-> 

 

1. 동일 계열의 알고리즘을 정의하고

동적으로 행위의 수정이 필요한 경우

전략을 바꾸는것으로 행위의 수정이 가능하게 만드는 패턴 

 

https://ko.wikipedia.org/wiki/%EC%A0%84%EB%9E%B5_%ED%8C%A8%ED%84%B4

 

example

public class StrategyPatternWiki
{
    public static void Main(String[] args)
    {
        Customer firstCustomer = new Customer(new NormalStrategy());

        // Normal billing
        firstCustomer.Add(1.0, 1);

        // Start Happy Hour
        firstCustomer.Strategy = new HappyHourStrategy();
        firstCustomer.Add(1.0, 2);

        // New Customer
        Customer secondCustomer = new Customer(new HappyHourStrategy());
        secondCustomer.Add(0.8, 1);
        // The Customer pays
        firstCustomer.PrintBill();

        // End Happy Hour
        secondCustomer.Strategy = new NormalStrategy();
        secondCustomer.Add(1.3, 2);
        secondCustomer.Add(2.5, 1);
        secondCustomer.PrintBill();
    }
}

명령 패턴

-> 

 

요청 자체를 캡슐화

실행될 기능을 캡슐화해 기능 실행을 요구하는 호출자와 실제 기능을 실행하는 수신자 클래스 사이의 의존성 제거

 

https://ko.wikipedia.org/wiki/%EC%BB%A4%EB%A7%A8%EB%93%9C_%ED%8C%A8%ED%84%B4

 

/*the Invoker class*/
public class Switch {
    private Command flipUpCommand;
    private Command flipDownCommand;

    public Switch(Command flipUpCmd,Command flipDownCmd){
        this.flipUpCommand=flipUpCmd;
        this.flipDownCommand=flipDownCmd;
    }

    public void flipUp(){
         flipUpCommand.execute();
    }

    public void flipDown(){
         flipDownCommand.execute();
    }
}

/*Receiver class*/

public class Light{
     public Light(){  }

     public void turnOn(){
        System.out.println("The light is on");
     }

     public void turnOff(){
        System.out.println("The light is off");
     }
}


/*the Command interface*/

public interface Command{
    void execute();
}


/*the Command for turning on the light*/

public class TurnOnLightCommand implements Command{
   private Light theLight;

   public TurnOnLightCommand(Light light){
        this.theLight=light;
   }

   public void execute(){
      theLight.turnOn();
   }
}

/*the Command for turning off the light*/

public class TurnOffLightCommand implements Command{
   private Light theLight;

   public TurnOffLightCommand(Light light){
        this.theLight=light;
   }

   public void execute(){
      theLight.turnOff();
   }
}

/*The test class*/
public class TestCommand{
   public static void main(String[] args){
       Light light=new Light();
       Command switchUp=new TurnOnLightCommand(light);
       Command switchDown=new TurnOffLightCommand(light);

       Switch s=new Switch(switchUp,switchDown);

       s.flipUp();
       s.flipDown();
   }
}

 

둘의 차이점?

 

개인적으로 둘의 쓰임새는 비슷하다고 느껴서 엄청난 혼동이 왔습니다 

 

스텍오버플로우 형님들에 따르면 

 

Command - Open or Close [action change]

Typically the Command pattern is used to make an object out of what needs to be done

Strategy - Quicksort or Mergesort [algo change]

The Strategy pattern, on the other hand, is used to specify how something should be done

 

라는데 요약해보자면

 

커맨드 패턴 -> 다양한 action이 있고 어떤 action'(what)을 사용하냐에 중점

 

전략 패턴 -> 다양한 algorithm이 있고 '어떻게'(how) 해결하냐에 중점

 

인거 같아요.?

 

 

reference

 

 

https://gmlwjd9405.github.io/2018/07/07/command-pattern.html

 

[Design Pattern] 커맨드 패턴이란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

https://stackoverflow.com/questions/4834979/difference-between-strategy-pattern-and-command-pattern

 

Difference between Strategy pattern and Command pattern

What is the difference between the Strategy pattern and the Command pattern? I am also looking for some examples in Java.

stackoverflow.com

 

반응형
반응형

프로세스를 새로 생성할때 fork 함수를 이용할 수 있습니다.

 

fork 함수를 호출한 프로세스는 부모 프로세스가 되고

새롭게 생성되는 프로세스는 자식 프로세스가 됩니다.

두 프로세스는 다른 pid를 가지고 독립적이게 동작합니다.

Copy On Write

 

부모 프로세스가 자식 프로세스를 생성(fork)한 직후 프로세스와 메모리 모습

이때 Linux에서는 자식 프로세스를 생성하면 같은 메모리 공간을 공유하게 됩니다. 

부모 프로세스에서 페이지 바로 C를 수정한다면 자식 프로세스에도 영향을 끼칠것입니다.

 

그래서 직접 변경 대신 페이지 C를 복사해서 수정(Copy on write)하는 방식으로 작동합니다.

 

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>

int statofLoc; // get child process's state when child process is done
int childPid;

char **command; //get parsed words, command[0] is command,  
                //others option

void fatal_error(char* errorCommand) // to record error and exit
{
  perror(errorCommand);
  exit(-1);
}

childPid = fork();

if(childPid == 0){ // child
			
   if(execvp(command[0], command)<0)
  {
    fatal_error("unknown command");
    //if this function is actived,
    //something is wrong
  }
}
else if(childPid < 0) //error
{
  fatal_error("pid ERROR");
}
else //parent
{
  waitpid(childPid, &statofLoc, WUNTRACED);
  //wait till child process is done.
}

 

wait(혹은 waitpid) 함수를 이용하면 부모 프로세스는 자식 프로세스가 종료될때까지 기다릴 수 있습니다.

 

wait는 다음과 같이 작동합니다.

 

1. 자식 프로세스가 동작 중이면 호출 차단이 차단되기 때문에 상태를 얻어올 때까지 대기

2. wait() 함수 호출자가 시그널을 받을 때까지 대기

3. 자식 프로세스가 종료된 상태라면 즉시 호출이 반환되어 상태를 얻음, 이 때 wait() 함수는 자식 프로세스의 프로세스 ID를 반환
4. 자식 프로세스가 없다면 호출이 즉시 반환되며, 에러값을 반환

 

고아 프로세스  

 

부모가 먼저 자식 프로세스보다 먼저 종료되면 자식 프로세스는 고아 프로세스가 됩니다.

 

만약 부모가 먼저 종료되면 커널은 부모 프로세스가 누구의 부모인지 확인하고 자식 프로세스들의 부모 프로세스 ID

(ppid)들을 전부 1(init process)로 바꿔줍니다.

 

리눅스에서 백그라운드에서 돌아가는 데몬 프로세스를 생성하는 방법으로 해당 방식을 이용합니다.

1. fork후 부모 종료(ppid 가 1로 변경)

2. setsid로 고아 프로세스를 세션 리더로 생성

3. etc...

 

고아 프로세스가 종료되면 init process가 wait를 호출해 좀비 프로세스가 되는것을 방지합니다.

좀비 프로세스

 

자식 프로세스가 종료되었지만 자식 프로세스의 종료상태를 회수하지 않았을때 자식 프로세스를 좀비 프로세스라 합니다.

 

자식 프로세스가 exit 시스템콜을 호출하면 프로세스에 대한 메모리 & 리소스가 해제되 다른 프로세스에서 사용 가능합니다.

그런데 자식 프로세스의 정보를 알고 싶어할 수 있기 때문에 부모 프로세스의 wait system call 호출 이전까지는 최소한의 정보를 가지는 상태로 남아 있습니다. 

wait로 정말로 제거되기 이전 상태를 좀비 프로세스라고 부릅니다. 

 

 

그럼 fork를 어디에 쓰나요..? 보통 멀티테스킹을 위해 많이 쓰입니다.

대표적인 예시로는 shell에서

 

터미널에서 fork 해 자식 프로세스를 만든 다음

자식 프로세스에서 명령어를 파싱하고, exec system call을 해 명령을 처리합니다.

 

다만 cd(change directory)의 경우 자식 프로세스가 아니라 부모 프로세스(터미널) 자체가 움직여야 하기 때문에

cd는 예외적으로 fork를 하지않고 직접 처리합니다.

 

reference

 

COW

https://openwiki.kr/tech/copy-on-write

 

Copy-on-write - 기술 - 오픈위키

Copy-on-write Copy On Write란 말 그대로 작성시 이전의 내용을 Copy한다는 뜻이다. 1) Linux(Unix)에서는 자식 프로세스(child process)를 생성(fork)하면 같은 메모리 공간을 공유하게 된다. 그런데 부모 프로세

openwiki.kr

 

https://codetravel.tistory.com/30?category=993122 

 

wait 함수 사용하여 자식 프로세스 종료시까지 대기하기

부모 프로세스가 fork() 함수를 사용하여 자식 프로세스를 생성하였을 때, fork() 함수가 리턴되는 시점부터 2개의 프로세스가 동작하게 됩니다. 부모 프로세스가 자식 프로세스의 종료 상태를 얻

codetravel.tistory.com

데몬 프로세스

https://kukuta.tistory.com/386

 

[Linux] 데몬(Daemon) 프로세스 만들기

데몬(daemon)이란? '데몬(daemon)' 프로세스는 리눅스 운영 체제서 사용하는 프로세스의 일종으로써, 시스템 시작이 시작할 때 그 생명을 시작하여, 우리가 알지 못하는 백그라운드에서 자신의 할 일

kukuta.tistory.com

 

반응형
반응형

출처 https://ko.wikipedia.org/wiki/%EC%A4%91%EC%9E%AC%EC%9E%90_%ED%8C%A8%ED%84%B4

 


객체지향 모델에서는 객체를 구성할때 행동을 여러 객체로 분산시켜 처리하도록 권하고 있습니다(SRP)
그러다보니 시스템의 객체 분할이 객체 간 상호종속을 유발하고 이때문에 재사용성이 저하되거나 결합도가 증가할 수 있습니다.

그러면 객체는 독립적이야한다는 객체의 특성이 유명무실해질 수 있는데 객체들간의 직접적 통신을 제한하고
중재자 객체를 통해 서로 양방향 통신하게 하는 디자인 패턴입니다.

예를 들어 숫자를 increment하는 단순한 board를 만든다고 해봅시다.
text class, InputBar class, button class가 있고 서로 상호작용하지 않고 mediator 패턴으로 구현해봅시다.

역할은 단순하게

text : 지금 숫자가 얼만지 보여줍니다(show)

button : 누를때마다 숫자가 1씩 증가합니다
혹은 reset 가능합니다

InputBar : input으로 넣은 숫자로 갱신됩니다.

class Button {
  constructor(mediator) {
    this.mediator = mediator;
    this.points = 0;
  }

  increment() {
    this.mediator.increment();
  }

  reset() {
    this.mediator.reset();
  }
}

class InputBar {
  constructor(mediator) {
    this.mediator = mediator;
  }

  update(val) {
    this.mediator.update(val);
  }
}

class TextBar {
  constructor(mediator) {
    this.text = 0;
    this.mediator = mediator;
  }

  setText(val) {
    this.text = val;
  }

  show() {
    this.mediator.show(this.text);
  }
}


mediator 객체는 다음과 같습니다.

class BoardMediator extends Mediator {
  constructor() {
    this.button = new Button(this);
    this.inputBar = new InputBar(this);
    this.textBar = new TextBar(this);
    this.points = 0;
  }

  increment() {
    this.points += 1;
    this.textBar.setText(this.points);
    console.log(`increment ${this.points}`);
  }

  update(val) {
    this.points = val;
    this.textBar.setText(this.points);
    console.log(`update ${this.points}`);
  }

  reset() {
    this.points = 0;
    this.textBar.setText(this.points);
    console.log(`reset ${this.points}`);
  }

  show(val) {
    console.log(`show ${val}`);
  }
}


실행을 시뮬레이션 해보았을때

const med = new BoardMediator();

// click mocking event

med.button.increment();
med.button.increment();
med.button.reset();
med.button.increment();
med.inputBar.update(10);
med.button.increment();
med.textBar.show();

(인터넷을 뒤져보니 mediator 구조에 emit 처럼 observer pattern을 하나 더 끼워넣은 구조도 많이 보이던데 그런 방식도 괜찮은거 같아요)


객체간 통신(N:M)을 객체끼리 직접 상호작용하는게 아니라 mediator 객체를 통해서 실행시키므로 결합도를 여러개의 1:N 정도로 크게 줄였습니다.

 

단점으로는 

중재자 객체가 엄청 비대해질 가능성이 있겠죠. 

또한 비대해지면 유지보수가 힘들어질 느낌이 듭니다. 


느낌상
observer pattern은 1:N이고 단방향
퍼사드 패턴은 N:M이고 중재자 패턴이랑 비슷한 용도로 쓰이기는 하는데

단방향이고 관계를 단순화하고 통합된 구조에 좀 더 관심이 있는 구조

(한 이벤트가 발생하면 내부 객체들이 정해진 구조에 따라서 알맞게 제어되고 실행되는 통합된 구조를 만드는데 관심이 있는 구조)


그리고 mediator 패턴은 N:M이고 양방향을 가질 수 있는 구조 느낌?

reference

GOF의 디자인 패턴

반응형
반응형

 
데코레이터 패턴이란 구조 패턴중의 하나로 상속(inheritance)과 합성(Composition)을 통해
기존 객체에 동적으로 동작을 추가하는 구조적인 패턴입니다.
 

새로운 서비스의 추가가 필요할때 일반적으로 상속을 이용합니다.

하지만 상속을 사용한다면 다음과 같은 문제점이 발생할 수 있습니다.

 

1. 나중 추가사항이 생긴다면 superclass를 수정하고 기능을 집어넣어야 합니다.

 

2. 1번으로 인하여 superclass는 비대해지고

SOLID의 SRP를 위반합니다. (어떤 특정 subClass A에서만 사용하는 메소드가 B, C에도 상속됩니다.)

 

그래서 OCP의 컨셉을 활용하여 해결하려고 시도하는 디자인 패턴입니다.

 

예를 들어 라면집을 하기 위해서 라면을 구현한다고 해봅시다.
라면엔 면과 다시마가 들어간다고 볼 수 있습니다.

 

class Food {
  add() {
    //재료 추가
  }
}

class Ramen extends Food {
  addKelp() {}

  addNoodle() {}

  add() {
    this.addNoodle();
    this.addKelp();
  }
}

 

그런데 라면의 인기가 높아져서 라면에 온갖 고추장, 치즈, 짜장, 곰탕등 소스를 집어넣기 시작했습니다.

그리고 각 소스를 그냥 넣는것이 아니라, 1개 이상 중복해서 넣어서 섞어먹는 이상한 사람들이 등장하기 시작했습니다.

 

이때 상속으로 구현하게 된다면 중복으로 쓰이는 메소드들은 상위 클래스인 Ramen class 혹은 어딘가에 구현이 되고, 상
속을 시켜야할것입니다.
예를 들어 치즈짜장라면과 치즈곰탕라면은 addCheese라는 공통 함수를 Ramen Class에 넣거나 CheeseRamen Class에 넣어야할것인데 그렇다면
 
1. 공통 class인 Ramen에 쓰이면 치즈를 안넣는 class도 상속을 받아서 문제가 발생
2. CheeseRamen Class에 넣는다면 조합에 따라 만들어지는 class의 개수가 기하급수적으로 증가하게 될것

 

입니다. 

 

그래서 데코레이터를 사용해서 치즈짜장라면과 치즈곰탕라면을..

상속을 사용하지 않고 데코레이터를 사용해서 구현해봅시다...

(글 쓰면서 뭔가 내용이 산으로 가는 느낌인데..?)

 

class Food {
  add() {
    //재료 추가
  }
}

class Ramen extends Food {
  addKelp() {
    return 'add Kelp ';
  }

  addNoodle() {
    return 'add noodle ';
  }

  assemble() {
    return this.addNoodle() + this.addKelp();
  }
}

class RamenDecorator extends Food {
  constructor(Ramen) {
    super();
    this.Ramen = Ramen;
  }

  assemble() {
    return this.Ramen.assemble();
  }
}

 

우선 Base가 되는 Ramen과 Ramen Decorator입니다.

RamenDecorator는 만들고 싶은 Ramen을 주입받아 조립해 사용합니다.

 

치즈라면을 일단 만들어 봅시다.

 

class CheeseRamenDecorator extends RamenDecorator {
  addCheese() {
    return 'add cheese';
  }

  assemble() {
    return super.assemble() + this.addCheese();
  }
}


const CheeseRamen = new CheeseRamenDecorator(new Ramen());
console.log(CheeseRamen.assemble());

 

그림으로 보면 이런 상태입니다.

$ node test.js
add noodle add Kelp add cheese

코드를 실제로 돌려보면 잘 적용된걸 확인할 수 있습니다.

 

이제 아까 말했던 치즈짜장라면과 치즈곰탕라면을 구현해봅시다.
class BlackbeanSauceRamenDecorator extends RamenDecorator {
  addBlackbeanSauce() {
    return 'add BlackbeanSauce';
  }

  assemble() {
    return super.assemble() + this.addBlackbeanSauce();
  }
}

class BeefBoneSoupRamenDecorator extends RamenDecorator {
  addBeefBoneSoup() {
    return 'add BeefBoneSoup';
  }

  assemble() {
    return super.assemble() + this.addBeefBoneSoup();
  }
}


const CheeseRamen = new CheeseRamenDecorator(new Ramen());
console.log(CheeseRamen.assemble());

const CheeseBlackbeanSauceRamen = new BlackbeanSauceRamenDecorator(
  new CheeseRamenDecorator(new Ramen()),
);
const CheeseBeefBoneSoupRamen = new BeefBoneSoupRamenDecorator(
  new CheeseRamenDecorator(new Ramen()),
);

console.log(CheeseBlackbeanSauceRamen.assemble());
console.log(CheeseBeefBoneSoupRamen.assemble());

아.. 라면 이름 예시를 잘못들었는데 지금 돌이킬순 없는거 같아요..

 

그림으로 보면 이런상태입니다. 

 

add noodle add Kelp add cheeseadd BlackbeanSauce
add noodle add Kelp add cheeseadd BeefBoneSoup

코드를 실제로 돌려보면 잘 적용된걸 확인할 수 있습니다.

그리고 상속으로 구현했을때보다 훨씬 깨끗해진걸 볼 수 있습니다.

 

지금은 조합 예시가 2~3개이지만 프로그램이 발전되며 조합이 수십~수백가지로 늘어났을때에도 상속보다 유연하게 처리 가능해진걸 확인할 수 있었습니다.

 

 

tmi 하나 하자면.. K사 기술면접에서 super class에 공통 함수를 놔둔다면 안쓰이는 subclass에서는 이것을 어떻게 처리해야할것인가라는 질문이 나왔었는데 그 당시에는 답변을 못했다가 오늘 알게됬네요 ㅠㅠ

 

reference 

 

https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html

 

[Design Pattern] 데코레이터 패턴이란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

 

https://steady-coding.tistory.com/391

 

[디자인 패턴] 데코레이터(Decorater) 패턴이란?

안녕하세요? 제이온입니다. 저번 시간에는 상태 패턴에 대해서 알아 보았습니다. 오늘은 데코레이터 패턴을 설명하겠습니다. 데코레이터(Decorater) 패턴 데코레이터 패턴은 객체에 추가적인 요건

steady-coding.tistory.com

 

반응형
반응형

엄청 헷갈리는 개념이라 메모해두려고 한다.

 

 

URI (Uniform Resource Identifiter - 통합 자원 식별자)

 

웹 기술에서 사용하는 논리적 or 물리적 리소스를 식별하는 고유한 문자열 시퀀스

주소보다는 '식별자'의 개념

 

URL (Uniform Resource Locator)

 

네트워크 상에서 웹 리소스에 대한 참조

흔히 웹 주소라고 한다.

우리가 주로 쓰는 주소가 URL

 

URL은 URI의 서브셋이다.

URI - 식별자

URL - 위치

 

 

URN (Uniform resource name)

 

URI의 특정 포맷중 하나로 이름으로 리소스를 특정하는 URI 

프로토콜을 제외하고 리소스의 name 을 가리키는데 사용

URN에는 접근 방법 & 위치가 표기 X

 

 

참고) URL 구조

 

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]

 

scheme : 사용할 프로토콜을 뜻하며 웹에서는 http 또는 https를 사용

user와 password : (서버에 있는) 데이터에 접근하기 위한 사용자의 이름과 비밀번호

host와 port : 접근할 대상(서버)의 호스트명과 포트번호

path : 접근할 대상(서버)의 경로에 대한 상세 정보

query : 접근할 대상에 전달하는 추가적인 정보 (파라미터)

fragment : 메인 리소스 내에 존재하는 서브 리소스에 접근할 때 이를 식별하기 위한 정보

 

reference

 

https://www.charlezz.com/?p=44767 

 

URI랑 URL 차이점이 뭔데? | 찰스의 안드로이드

URI 그리고 URL을 혼용해서 사용하는 경우가 있다. 대부분의 경우 문제가 없지만 정확하게 이 둘의 차이점이 존재한다. 그러므로 각 용어의 정의와 용도에 대해서 알아본다. URI URI는 특정 리소스

www.charlezz.com

 

https://mygumi.tistory.com/139

 

URI vs URL vs URN :: 마이구미

이번 글은 URI, URL, URN 을 다뤄본다. URI와 URL은 아직도 많이 혼동되고 있다. 우리는 대부분 URL이라는 표현을 하고 있다. 우리가 보고 있고, 사용하고 있는 대부분이 사실 URL이기 때문이다. URI, URL, U

mygumi.tistory.com

 

반응형

'CS > 네트워크' 카테고리의 다른 글

AnyCast란?  (0) 2022.04.11
HTTP 메소드 멱등성, 안전한 메소드  (0) 2022.01.31
[TCP] 3-way handshake와 4-way handshake  (0) 2022.01.23
Pooling, Long Pooling, Streaming  (0) 2022.01.23
반응형

개인적으로 DB 내용은 어렵기도 하고 안쓰고 있으면 자꾸 까먹는거 같습니다.

이 블로그의 주 목적이기도 하고 메모용으로 간단하게 글을 써보려 합니다.

 

*틀린게 있으면 피드백 주시면 감사하겠습니다!

 

index란?

 추가적인 쓰기 작업과 저장 공간을 활용하여 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조

1000페이지의 책이 있다고 쳐봅시다. 특정 키워드를 찾고 싶은데 1쪽부터 1000쪽까지 전부 순회하며 찾으려면 매우 큰 시간 낭비일것입니다.

 

그래서 책에는 키워드를 빨리 찾기 위해서 보통 맨뒤에 색인이 있습니다.

데이터베이스에서도 인덱스는 책의 색인과 비슷한 역할을 한다고 보면 좋습니다.

 

(보통 Index는 PK에 기본적으로 걸려있습니다.)

 

Index의 구조

 

Balanced Tree인 B+(B) tree를 많이 사용합니다.

 

A: 트랙 B: 기하학적 섹터 C: 트랙 섹터 D: 클러스터

왜 tree 형태냐면 하드 디스크에서 데이터를 가져올때 섹터 단위(block 단위)로 가져오게 되는데 데이터를 효율적으로 관리하기 위해서 포인터로 참조하는 트리 형태를 띄게 됩니다.

 

*  b tree는 모든 노드에 key와 데이터를 가지지만 B+ tree는

branch node에서는 leaf node에 대한 key를 가지고, leaf node에만 데이터를 가집니다. 

 

Index의 장점

 

- 테이블 조회 속도 & 성능 향상

- 시스템 부하 감소

 

Index의 단점

 

- 인덱스를 사용하기 위해 대략 10%의 추가 저장공간이 필요하다

- 잘못 사용할 경우 성능 저하

- index 관리를 위해 추가 작업 필요 

 

보통 create, update, delete는 빈번하게 일어나지 않고 조회(select)만 자주 일어나는 속성에 사용하는게 좋습니다

-> DELETE, UPDATE 시 인덱스를 '사용하지 않음' 처리 

빈번하게 DELETE, UPDATE가 일어난다면 인덱스의 크기가 매우 커져서 오히려 성능이 줄어든다.

 

* select문에서 like '%문자' 같이 %를 앞에 넣어서 사용하는 경우 index를 타지않아서

검색 구현시 형태소 분석이나 elasticsearch를 사용하는것을 권장한다고 합니다

 

사용하면 좋은경우 

 

- 규모가 매우 큰 테이블

- INSERT, UPDATE, DELETE가 빈번하지 않은 컬럼

- 자주 SELECT문(특히 join, where, order by)에 쓰이는 컬럼

- 데이터의 중복도가 낮은(=cardinality가 높은) 테이블

- 데이터 분포도 10~15%(1/해당 컬럼의 distinct 데이터 개수 * 100)인 칼럼

 

Cardinality란?

 

전체 행에 대한 특정 컬럼의 중복 수치를 나타내는 지표입니다

 

-> 중복도 낮다 = 카다널리티 높다.

-> 중복도 높다 = 카다널리티 낮다.

 

중복도는 어떤 절대적인 수치가 아니라 '상대'적인 수치입니다.

id name location
1 park seoul
2 lee seoul
3 park busan
4 kim busan

 

id가 distanct값이 4개이므로 가장 카다널리티가 높습니다.

name은 distant 값이 3개이므로 id보다는 카다널리티가 낮고, location보다는 카다널리티가 높습니다.

location은 distant 값이 2개이므로 가장 카다널리티가 낮습니다.

 

인덱스를 걸때 중복이 최대한 없어야 데이터가 많이 걸러질것입니다. (중복이 많을수록 full scan에 가까워짐)

그래서 카다널리티가 높은 컬럼을 인덱스 우선순위로 추천한다고 합니다. 

 

Clustered Index 

 

Index를 생성할때 데이터 페이지 전체를 물리적으로 재배열 합니다.

 

테이블 당 한개만 존재합니다.

-> 가장 효율적인 칼럼을 Clustered Index로 지정 

테이블 크기의 3%정도 추천

 

Non-Clustered Index

 

군집화 되어있지 않은 인덱스 타입? (순서대로 정렬되있지 않다)

물리적으로 데이터를 배열하지 않고, 별도의 장소에 데이터 페이지가 구성됩니다.

테이블 당 여러개 존재 가능합니다.

 

Non-clusered index는 clustered Index보다 검색 속도는 느리지만 입력/수정/삭제는 더 빠릅니다.

(clustered Index의 경우 그 반대)

테이블 크기의 20%정도 추천

 

reference 

 

index 

 

https://mangkyu.tistory.com/96

 

[Database] 인덱스(index)란?

1. 인덱스(Index)란? [ 인덱스(index)란? ] 인덱스란 추가적인 쓰기 작업과 저장 공간을 활용하여 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조이다. 만약 우리가 책에서 원하는

mangkyu.tistory.com

 

b tree 

 

https://velog.io/@seanlion/btree

 

B트리,B+트리, B*트리 개념 정리

오늘은 트리 종류 중 하나인 B트리 시리즈를 정리해보려고 합니다. 이 포스팅에서는 B트리 시리즈 개념에 대해서 다룹니다.

velog.io

 

카디널리티

 

https://itholic.github.io/database-cardinality/

 

[database] 카디널리티(cardinality)란?

cardinality

itholic.github.io

 

반응형

+ Recent posts