728x90

In Typescript, the exclamtion is sometimes used in last of the characters.

For example,

const people = { name: 'kim' };

if (people.name!) {
  ...
}

what is meaning?

In TypeScript, the exclamation mark (!) is used to assert that a value is not null or undefined. It is called the non-null assertion operator. However, it is typically used when you are certain that the value will not be null or undefined, and it should be used with caution.

728x90
반응형

'개발, 코딩 > typescript' 카테고리의 다른 글

typescript, 제네릭에대해 알아보자  (0) 2022.11.10
optional과 undefined의 차이  (0) 2022.10.26
typescript, 타입 소개  (0) 2022.10.26
728x90
// < 18
type TagPrpos = {
  name: string,
};

const Tag: React.FC<TagProps> = ({ name }) => (
  <div>
    <div>{ name }</div>
    { children }
  </div>
);
// > 18
type TagPrpos = {
  name: string,
  children: react.ReactNode
};

const Tag: React.FC<TagProps> = ({ name, children }) => (
  <div>
    <div>{ name }</div>
    { children }
  </div>
);

 

children 요소를 명시적으로 선언해줘야한다.

< 18에서는 children이 optional로 선언되어있었던 반면,

18에서는 optional 선언도 제거되었기 때문이다.

728x90
반응형
728x90

class변수를 어떤 방식으로 선언할지 고민해보게되는 계기가 있었다.

class변수에는 총 세가지가 있다
1. static 변수
2. instance 변수
3. local 변수

각각의 특성에 대해 알아보고 언제 어떻게 선언하는게 프로그램에 도움이 되는지 고민해보자.

1. static변수

class HumanLifeCycle {
  static morningRoutine = 'coffee';
  ...
}

위에서 보이는 static키워드를 이용한 선언 방식이 static변수이다. 변수를 static으로 선언하게 되면 해당 클래스러 몇개의 인스턴스를 생성하던지 상관없이 메모리상 하나의 변수만 참조하게된다. 따라서, 메모리를 절약할수 있다는 장점이 있다.
그렇다고 늘 static으로 선언해서는 안된다. 단 하나의 변수라는 개념인 만큼 여러 인스턴스에서 하나의 값을 공유하게 되기 때문에 각 인스턴스가 독립적으로 변수를 다뤄야 한다면 static을 써서는 안된다.
static이란 이름에 걸맞게 정적이고(변동이 없고) 클래스 인스턴스 간 글로벌하게 쓰이는 경우에 static변수를 쓰도록 하면 어떨까?

2. instance변수

class HumanLifeCycle {
  constructor() {
    this.pattern = {
      morning: '',
      afternoon: '',
    };
  }
}

위 코드에서 보이는 this.pattern이 instance변수에 해당한다. new연산자를 통해 인스턴스를 생성할때 마다 메모리에 해당 변수에 대한 공간을 할당한다. 즉, 인스턴스간 독립적인 변수를 갖게된다.
static이 클래스 레벨에서 변수를 공유하는것과 달리 인스턴스 레벨에서 변수를 다룬다는 차이가 있다. 메모리에 예민한 프로그램이 아니라면 직관적인 instance변수를 주로 즐겨쓰지만 최고의 프로그래머가 되려면 최적화에 대해 더 고민해볼 필요가 있을것 같다.

3. local변수
클래스 내 메소드에서 선언한 변수에 해당한다. 메소드 내에서 잠시 값을 담아두기 위해 쓰이곤 한다.

728x90
반응형
728x90

제네릭
보기에도 이상하고 워딩도 애매서
단어만 들었을땐 거부감이 좀 든다

하지만, any를 남발하지 않으려면
제네릭은 반드시 알아야만 할것으로 생각된다.

분명 많은 개발자들이 any를 쓰는 그 순간까지
아 이거 그런거 없나..
하고 생각했을텐대
알고보면 바로 그게 제네릭이다
(나만 그랬나..?)

제네릭에대해 바로 알아보자.
제네릭을 정의 하자면 "아직은 정해지지 않았지만 쓰는 사람이 쓰는 타입에 따라 유동성있게 정해지는 타입임"을 의미한다.

일단, 제네릭 없이 각 기계장치에 엔진을 간단 붙여본다.

interface Vehicle {
  run() => void;
}

class Car implements Vehicle {
  run(runTime: number) {
    console.log('4 wheels drive...', runTime);
  }
  
  changeWheel(wheelNumber: number) {
    if (wheelNumber > 4)
      throw new Error('You have only 4 wheels...');
      
    console.log('changed whleel');
  }
}

class Airplane implements Vehicle {
  run(runTime: number) {
    console.log('2 wings drive...', runTime);
  }
  
  fixWing(wingPosition: 'left' | 'right') {
    console.log('fix..!');
  }
}

function doPreRun(vehicle: Vehicle): Vehicle {
  vehicle.run(30);
  return vehicle;
}

const genesis = new Car();
genesis.changeWheel(3); // ok

doPreRun(genesis);
genesis.changeWheel(4); // failed error

위 코드에서 보다시피
예열이 되어 반환된 인스턴스는
본인의 정체성을 잃고 interface의 구현만 갖는다.
doPreRun함수가 Vehicle 인스턴스를 리턴하기 때문이다.

이러한 상황을 피하고 보다 유동성있고 추상화하여 작성하고 싶다?
이럴때 바로 제네릭이 필요하다
제네릭을 이용해 코드를 작성해본다.

function doPreRun<T>(vehicle: T): T {
  vehicle.run(30);
  return vehicle;
}

const genesis = new Car();
genesis.changeWheel(3); // ok

doPreRun(genesis);
genesis.changeWheel(4); // ok

T라는 캐릭터를 썻는데 어려워할 필요없다.
임의의 T(Type)이라는 것이니까
다른 표현으로 V(Value), K(Key) 등이 많이 쓰인다고한다.


조금만 더 나아가보자.
지금은 T라는 타입이 너무 광번위 하여
다른 개발자가 Vehicle이 아닌 인스턴스를 넘길 위험이 존재해 보인다.

function doPreRun<T extends Vehicle>(vehicle: T): T {
  vehicle.run(30);
  return vehicle;
}

const genesis = new Car();
genesis.changeWheel(3); // ok

doPreRun(genesis);
genesis.changeWheel(4); // ok

이렇게
Vehicle을 확장(상속)하여 구현한 타입만을
허용하도록 수정할 수 있다.

제네릭을 더욱 유용하게 쓰기위해
constrains라는 것에 대해 알아볼것이다.
constrains는 제네릭에 조건을 거는것이다

const obj1 = {
  name: 'mocar',
  age: 31,
};

const obj2 = {
  hasPen: true,
  hasNote: false,
};

const obj3 = {
  cash: 3000,
  debt: 1000000,
};

const getValue<T, K extends keyof T> = (obj: T, key: K): T[K] => {
  return obj[key];
}

어떤가
알고보면 그렇게 어렵지않고
매우 유용한것이다.

728x90
반응형

'개발, 코딩 > typescript' 카테고리의 다른 글

Typescript, "people.name!" - what is meaning?  (0) 2023.06.05
optional과 undefined의 차이  (0) 2022.10.26
typescript, 타입 소개  (0) 2022.10.26
728x90

함수형 프로그래밍, 방어적 프로그래밍 등

다양한 영역에 걸쳐서

순수함수를 작성할 것을 강조한다.

 

순수함수란 무엇인가?

const total = sum(2, 8);

function sum(a, b) {
  return a + b;
}

여기 sum이라는 함수처럼

부수효과가 없는 함수 

즉, 어떤 함수에 동일한 인자를 주었을 때 항상 같은 값을 리턴하는 함수

 

부수효과?

그것의 의미는 바로,

외부의 상태를 변경하는 것

또는 함수로 들어온 인자의 상태를 직접 변경하는 것

을 의미한다.

 

순수함수가 아닌 경우의 sum

const total = sum(2, 8);
let favoriteNumber = 4;

function bugSum(a, b) {
 const sum = a + b;
 favoriteNumber = sum;
 return sum + favoriteNubmer;
}

 

위와 같이 함수를 작성하면

언제 프로그래밍에 버그를 만들어낼지 모른다.

나 혼자 개발한다면

혼자 잘 주의해서 개발하면 괜찮지만

 

다른 개발자가

내 코드를 받아서 사용할때

부수효과를 만들수 있는 favoriteNumber를

직접 조작하는 경우 등

에러요인이 만들어 질 가능성이 존재한다.

 

순수함수를 작성하도록 노력하자!

728x90
반응형

'개발, 코딩' 카테고리의 다른 글

객체지향 프로그래밍 핵심 개념  (0) 2022.10.27
git 기본기_백과사전(2)  (0) 2022.10.26
git 기본기_백과사전 (1)  (0) 2022.10.24
git 협업하기 - 실무편  (0) 2022.10.23
Webpack 구성 이해하기  (0) 2022.10.04
728x90

명령형, 절차지향적 프로그래밍

앱을 구현하기위한 데이터와 함수로 구성됨
정의된 순서대로 함수가 구동하는 프로그래밍
구현을 위해서는 모든 코드를 이해해야하여
유지보수가 어렵고 에러찾기가 어렵다.


객체지향 프로그래밍

서로 관련있는 데이터와 함수를
여러가지 오브젝트(객체)로 구성하여
프로그래밍 해나가는 것.
문제가 생기면 관련된 오브젝트만 확인하면 되고,
기능을 추가한다면 새로운 오브젝트를 만들면 되어
유지보수가 쉬워진다.


오브젝트(객체)

데이터와 함수로 구성됨
하나의 객체로써 명사로 이름을 짓는다.
오브젝트 내 데이터는 fields/properties라고 부른다.
오브젝트 내 함수는 메소드라고 부른다.


클래스와 오브젝트

클래스는 오브젝트를 만드는 일종의 템플릿
템플릿(클래스)를 이용해 만든 객체가 오브젝트
이러한 객체를 클래스의 인스턴스라고 부른다.


객체지향 프로그래밍의 원칙

캡슐화 / 추상화 / 상속 / 다형성 이 4가지를
객체지향 프로그래밍의 4원칙 이라고 부른다.


캡슐화

여러 기능들 중 서로 연관성있는 기능들을
묶는것을 "캡슐화"

eg)
감기약 캡슐에 다양한 성분의 약이 있지만
캡슐로 감싸두고, 안에 무엇이 있는지 모르고 그냥 먹는것과 같음

eg)
우리가 고양이 내부의 상태를 직접 바꿀수 없다.
고양이와 놀아줌으로써 내부 상태를 바꿀수는 있다.

이렇듯 안에 있는 것은 직접 건드리지 않고,
외부에 공개된 기능을 통해 내부의 값을 변경하는 것
그리고 어떤것을 외부에 공개하고 공개 안할지 결정하는것

이러한 일련의 행위를 "캡슐화" 라고함
키워드: public / private / protected

멤버변수의 기본 상태: public
외부에 공개하고 싶지 않을때: private
외부에서는 접근 할수없지만 상속한 자식 클래스에서는 접근할수있게 해주는것: protected

추상화

내부의 복잡한 기능을 다 이해하지 않더라도
외부에서 내부가 어떻게 구현됐는지는 상관않고
외부에서 보이는 인터페이스 만으로 이용할 수 있는 것 다시 말하면,
우리가 자동차의 원리를 몰라도 엑셀과 브레이크만 알면 운전할수 있듯이 외부에서는 내부에 어떻게 구현됐는지 신경쓰지않고 인터페이스만을 통해 함수를 이용할 수 있도록 하는 것

추상화 전

type Animal = 'dog', 'cat', 'pig';
type PetSkill = 'give me hand', 'sit down', 'high five';

class Pet {
  private skills: PetSkill;
  
  cosntructor(public kind: Animal, public name: string) {
  }
  
  training(skill: PetSkill) {
    this.edu();
    this.charming();
    this.skills.push(skill);
  }
  
  charming() {
    ...
  }
  
  edu() {
    ...
  }
}



추상화 방법1 - 정보은닉

type Animal = 'dog', 'cat', 'pig';
type PetSkill = 'give me hand', 'sit down', 'high five';

class Pet {
  private skills: PetSkill;
  
  cosntructor(public kind: Animal, public name: string) {
  }
  
  training(skill: PetSkill) {
    this.edu();
    this.charming();
    this.skills.push(skill);
  }
  
  private charming() {
    ...
  }
  
  private edu() {
    ...
  }
}

const myPuppy = new Pet('dog', 'meongu');
myPuppy.training('give me hand');



추상화 방법2 - interface

type Animal = 'dog', 'cat', 'pig';
type PetSkill = 'give me hand', 'sit down', 'high five';

interface Pet {
  training(skill: PetSkill): void;
}

class FriendPet {
  private skills: PetSkill;
  
  cosntructor(public kind: Animal, public name: string) {
  }
  
  training(skill: PetSkill) {
    this.edu();
    this.charming();
    this.skills.push(skill);
  }
  
  charming() {
    ...
  }
  
  edu() {
    ...
  }
}

const myPuppy: Pet= new FriendPet('dog', 'meongu');
myPuppy.training('give me hand');



상속

상속을 이용하면 잘 정의해둔 클래스를 재사용하여 부모 클래스의 function, properties를 상속하여 새로운 클래스를 쉽게 만들어 낼 수있다.


다형성

상속을 받은 자식 클래스들은 자신이 무엇인지와 관계없이 부모 클래스의 function을 공통적으로 호출할 수 있다.

static

모든 인스턴스가 동일하게 가지는 멤버변수가 있다면 static을 붙여주자. 그러면 class레벨로 멤버변수가 변경되어, 매번 인스턴스를 만들때마다 메모리를 차지하지 않는다.

get/set

getter, setter를 활용하면
클래스 사용자는 멤버변수를 다루듯이
age를 쓸수있으면서, 내부적으로는 예외처리까지 할수있다.
이러한 큰 장점이 있어 많은 개발자들이 애용하고있다.
(어떤 개발자는 getter/setter가 몇몇 경우에 직관적이지 못하다 등의 이유로 쓰지 않는것을 권장하기도 한다.)

class Person {
  private nationalAge: number;
  constructor(private firstname: string, public lastname: string) {
  }
  
  get fullname(): string {
    return `${this.firstname}${this.lastname}`;
  }
  
  get age(): number {
    return age;
  }
  
  set age(num: number) {
    if (!num || typeof num !== number) {
      throw new Error('숫자를 넘기셔야지요!');
    }
    
    this.nationalAge = num;
  }
}

728x90
반응형

'개발, 코딩' 카테고리의 다른 글

순수함수 (pure function)  (0) 2022.11.06
git 기본기_백과사전(2)  (0) 2022.10.26
git 기본기_백과사전 (1)  (0) 2022.10.24
git 협업하기 - 실무편  (0) 2022.10.23
Webpack 구성 이해하기  (0) 2022.10.04
728x90

1. optional

function getName(first: string, last?: string): string {
  return first + last;
}

getName('lee'); // ok
getName('lee', undefined); // ok
getName('lee', 'webster'); // ok

 

2. undefined

function getName(first: string, last: string | undefined): string {
  return first + last;
}

getName('lee'); // error
getName('lee', undefined); // ok
getName('lee', 'webster'); // ok

 

위의 예시로 보아 알수있듯이

optional을 쓰지않고 undefined 를 쓰게되면

명시적으로 undefined로 선언해주어야만 한다.

728x90
반응형
728x90

1. string

문자열 타입

 

2. number

숫자

 

3. booelan

true/false

 

4. undefined

데이터 타입이 있는지 없는지 알수없는 경우에 주로 쓰이며

단독으로 쓰이기 보다는

string | undefined와 같이 쓰인다.

(| 는 유니온 타입으로 A타입 또는 B타입 처럼, 타입을 또는 이라는 키워드로 묶어준다.)

 

5. null

undefined과 유사하지만

값이 있는지 없는지에 대한것

주로, string | null 형태로 쓰인다

 

6. unknown 💩

any와 마찬가지로 어떤 type이든 올수있다.

무엇이 올지 알수없을때 사용되는데,

사실 typescript에서는 사용되지는 않고

javascript와 혼합해서 코드를 작성할때

무엇이 return되는지 알수없거나 할 때 사용한다.

 

7. any 💩

무엇이든, 어떤것이든 할당할 수 있는 타입이다.

정말 불가피한 경우에 도피처로 사용된다.

 

8. void

함수가 동작은 하지만

return이 없는 함수인 경우 혹은 return; 인 경우

void type에 해당한다.

 

9. never

함수에서 while: true, throw error와 같이

앱이 죽을 정도로 절대로 리턴이 발생할 수 없는 경우의

타입에 해당한다.

 

10. object 💩

어떤 타입의 object(객체)라도 받을 수 있는 타입이다.

심지어 배열도 가능하다.

 

11. Promise

함수에서 promise를 return하는 경우 사용되며

주로 Promise<number>와 같이

리턴되는 데이터를 제네릭으로 갖는다.

 

12. array

string[] 또는 Array<string>으로 정의할 수 있다.

readonly를 부여해서 수정 불가능하게 만들수도 있다.

const numbers: readonly number[] = [1,2,3];

function sum(numbers: readonly number[]) {
...
}

 

13. Tuple 💩

배열과 비슷하지만 각 자리에 오는 값의 타입을 정할 수 있다.

const items: [string, number] = ['pen', 100];

 

14. type alias

타입을 직접 정의하는것

type device = {
  laptop: string;
  phone: 'iphone' | 'galaxy';
};

 

15. String Literal

위에 alias타입에서 슬쩍 나온 부분이다.

type OS = 'ios';
const myPhone: OS = 'galaxy'; // error
const myPhone: OS = 'ios'; // ok

지정된 문자열 값만 허용하는 타입이나

 

16. union

위에서도 몇번 나왔지만

union타입이란 

A 또는 B 또는 C ... 와 같이

몇개의 타입중에 하나에 해당하는 것이라고 정의할 때 쓰인다.

type OS = 'linux' | 'macos' | 'window';

 

17. Discriminated union

alias와 union타입을 적절하게 이용하면,

보다 나은 개발을 할 수 있다.

type SUCCESS_RESPONSE = {
  result: 'success';
  body: {
    code: string;
    data: string;
  };
};

type FAILED_RESPONSE = {
  result: 'failed';
  reason: string;
};

type API_REPSONSE = SUCCESS_RESPONSE | FAILED_RESPONSE;

// res.result 자동완성 가능
function requestSomething(res: API_RESPONSE) {
  if (res.result === 'success') {
  } else {
  }
}

 

18. intersection

union이 or과 같다면

intersection은 and와 같다.

A타입 이면서 B타입인 경우에 사용한다.

type desk = {
  material: string;
  size: number;
};

type table = {
  color: string;
  height: number;
};

const multiDesk: desk & table = {
  material: '',
  size: 1200,
  color: 'white',
  height: 600,
};

 

19. Enum 💩

여러가지 관련된 상수를 정의할때 사용한다.

// js
const DAYS = Object.freeze({
  "MONDAY": 1,
  "TUESDAY": 2,
});

//ts - enum에 값을 지정하지 않으면 맨 위부터 0으로 초기화됨
enum DAYS {
  Monday,
  Tuesday
};

enum REST {
  POST = 'POST',
  GET = 'GET',
};

enum으로 타입을 설정했더라도

다른 값으로 값을 초기화 할수 있다는 이슈가 있다.

즉 타입이 보장되지 않는다.

const yesterday: DAYS = 1; // ok
const today: DAYS = 10; // ok..?

위와 같은 경우에는

유니온 타입으로 타입을 정의하는게 더 바람직하다.

 

20. type assertion 💩

타입에 대해 완전히 확신할때 타입을 강제로 캐스팅하여

타입의 메소드를 사용할 수 있다.

 

typescript에서는 거의 쓰이지 않지만

js코드와 병행해서 작업할때 종종 쓰일 수 있다.

function sum(a: number, b, number): number {
  return a + b;
}

const result = sum(1, 5);
(result as string).length; // undefined
(result as Array<number>).length // app crahsed
result!.length; // app crahsed, 완전히 타입에 대해 확신할때 !를 사용하여 warning을 제거할 수 있다

 

728x90
반응형
728x90

git stash

(working directory에) 작업중인 사항이 있는데

긴급히 다른 브랜치의 코드를 가져와 확인해야 한다면,

stash를 이용!

 

미완성 코드를 commit 하지 않고

stash stack에 임시로 보관했다가

다른 작업을 충분히 수행한 후,

내 코드를 다시 가져와 작업을 이어갈 수 있다.

 

혹은 버그를 잡기위한 시도를 하던중

잘 안됐더라도 의미 있는 작업을 했을때

임시 보관을 위해 stash에 저장해둘수도 있다.

# stash에 작업중인 사항 보관하기 - 1
git stash

# stash에 작업중인 사항 보관하기 - 2
git stash push

# msg와 함꼐 stash
git stash -m "<메세지>"

# 작업내역을 유지한채 stash에도 추가 (stash에 현재 작업내용 카피 떠두는 개념)
git stash -m "<메시지>" --keep-index

# untracking 파일포함 보관하기
git stash -u

# stash 목록 보기
git stash list

# stash 이력별 수정내용 보기
git stash show <stashId> -p

# stash 목록에서 최신 stash 다시 가져오기 (stash는 유지됨)
git stash apply

# stash 목록에서 선택해 stash 다시 가져오기
git stash apply <stashId>

# stash 목록에서 최신 stash 다시 가져오기 (stash는 제거됨)
git stash pop

# stash 제거
git stash drop <stashId>

# stash 전체 삭제
git stash clear

# 최신 stash를 가져오면서 새 브랜치에 적용
git stash branch <브랜치명>

 

git restore

working directory에 있는 작업중인 사항을 초기화

# working directory내 전체 초기화
git restore .

# 새로 추가된 파일은 restore로 제거되지 않음, 이때 사용하는 force directory
git clean -fd

# working directory내 특정 파일 초기화
git restore <파일명>

# add되어 staging area에 있는 파일을 working directory로 다시 가져옴
git restore --staged <파일명>

# add되어 staging area에 있는 모든 파일을 working directory로 다시 가져옴
git restore --staged .

# commit history로 초기화
git restore --source=<해시코드> <초기화할파일>

 

git commit --amend

commit 메시지를 잘못 적은 경우,

commit한 파일을 수정해야되는 경우,

직전에 수정한 commit을 수정할 수 있다

# 직전 커밋 메시지 수정하기
git commit --amend -m "<메시지>"

# 커밋한 파일의 내용을 수정해야할때, 추가 커밋없이 파일 수정
커밋했던 파일 현재 working directory에서 수정
git commit --amend

 

git reset

특정한 커밋으로 모든것을 초기화(지움) 할수있다.

원하는 버전의 해시코드로 reset하면 그 사이의 커밋은 제거된다.

 

--mixed

커밋 히스토리를 삭제하면서 해당 내용을

working directory로 작업 내역이 옮겨짐

 

--soft

커밋 히스토리를 삭제하면서 해당 내용을

staging area로 작업 내역이 옮겨짐

 

--hard

커밋 히스토리를 삭제하면서 해당 내용도 완전히 삭제

# 현재 ~ 해당 해시코드 사이의 commit 제거 + 내용은 working directory로 이동 - 1
git reset <해시코드>

# 현재 ~ 해당 해시코드 사이의 commit 제거 + 내용은 working directory로 이동 - 2
git reset --mixed <해시코드>

# 현재 ~ 해당 해시코드 사이의 commit 제거 + 내용은 staging area로 이동
git reset --soft <해시코드>

# 현재 ~ 해당 해시코드 사이의 commit 제거 + 내용은 삭제
git reset --hard <해시코드>

**주의사항

현재 working directory에 수정사항이 있는 상태에서

reset을 진행했다면 (특히, --hard)

작업중이 사항을 전부 잃을 가능성이 매우 높다

 

reset을 하기전에 working directory에 있는 파일들은

가능한 commit/stash 해두도록 하자.

 

git reflog

이전에 HEAD가 가리키고 있던 내역들을 전부 확인할 수 있다.

(내가 여태까지 실행했던 명령과 HEAD가 가리켰던 포인터들을 전부 확인 가능)

# 명령 이력 조회
git reflog

# 특정 해시코드로 돌아가기
git reset --hard <해시코드>

 

git revert

해당하는 커밋의 변경 사항을 완전히 취소할 수 있다.

명령후 revert 된 커밋이 새로 추가된다.

 

특히, 이미 서버 마스터 브랜치에 커밋인 경우

rebase, reset보다는 revert를 사용하는것이 맞다.

 

--no-commit을 쓸때, revert 커밋에서는 다른 파일을

수정, 삭제 등을 진행하는일은 절대로 없도록 한다.

# 특정 커밋 완전히 제거 + 취소사항 커밋
git revert <해시코드>

# 특정 커밋 완전히 제거 + 취소사항 staging area로 이동
git revert --no-commit <해시코드>

 

git rebase -i (interactive)

이전에 커밋된 사항을 수정할 수 있다.

이미 서버에 업로드된 사항은 다루지 말자.

 

오래된 순으로

5,4,3,2,1 이라는 커밋이 있을때

4라는 커밋을 수정하고 싶다면

4가 가리키는 이전 커밋인 5를 기준으로 해야한다.

 

rebase하게되면 기준 이후의 커밋들이

전부 수정되는 개념이기 때문이다.

(실제로, rebase 범주내 커밋들은 rebase후 해시코드가 변경됨)

# interactive rebase
git rebase -i <해시코드>

interactive 옵션

pick(p): 이거 써

reward(r): ok, 메시지는 변경

edit(e): ok, 변경사항 변경

squash(s): 여러가지 커밋 묶기 + 새 커밋 메시지

fixup(f): 여러가지 커밋 묶기

drop(d): 해당하는 커밋을 제거

 

 

git rebase & reset으로 커밋 분할

하나의 커밋에는 하나의 작업단위만 수행하는게 일반적이다.

예를들면, 버그수정 / 기능추가 / 라이브러리 추가 등.

그래야만 문제가 발생했을때 빠르게 revert해서 대응한다거나

문제의 커밋으로 되돌아가서 수정하는것이 수월하기 때문이다.

 

근데 나를 포함한 많은 개발자들이 실수하는게 여러개의

작업을 하나의 커밋에 추가하는 것이다.

 

실수를해야 사람이다.

실수로 여러 작업을 한 커밋을 다시 분할하는 방법에 대해 알아본다.

# A라는 비대한 커밋을 쪼개보자
# 1. A직전 커밋 해시코드 확인
git status

# 2. rebase interactive
git rebase -i <A직전 커밋 해시코드>

# 3. interactive - edit (커밋 수정하기 옵션)
pick -> e

# 4. reset으로 커밋을 working directory로 가져오기
git reset <가져올 해시코드>

# 5. 가져온 커밋에서 작업단위로 쪼개서 commit 하기
git add <특정 작업파일들>
git commit -m "<작업메시지>"
git add <특정 작업파일들>
git commit -m "<작업메시지>"

#6. continue
git rebase --continue

 

그런가 하면 commit들을 하나의 commit으로 묶는것도 가능하다.

# 1. 작업 범주 해시코드 확인
git status

# 2. rebase interactive
git rebase -i <squash할 범위 +1의 해시코드>

# 3. squash
squash(커밋 묶기)를 진행할때는 가장 최근 commit은 pick으로 유지한채
그외의 묶을 것들의 옵션을 s(squash)로 변경한다.

# 4. 커밋 메시지 작성하기
필요없는 내용은 지우고 커밋 메시지 작성

 

 

단, 혼자 작업하는게 아니라면

서버에 업로드된 작업에대해 절대로 rebase를 사용하지 말자

728x90
반응형

'개발, 코딩' 카테고리의 다른 글

순수함수 (pure function)  (0) 2022.11.06
객체지향 프로그래밍 핵심 개념  (0) 2022.10.27
git 기본기_백과사전 (1)  (0) 2022.10.24
git 협업하기 - 실무편  (0) 2022.10.23
Webpack 구성 이해하기  (0) 2022.10.04
728x90

git workflow 구성

1. working directory: 작업중인 로컬 환경, untracked/tracked으로 구분됨
2. staging area: git add를 통해 작업 내역이 올라가는 장소
3. git directory: git commit하면 작업 내역이 git directory에 위치됨
4. server(github/gitlab): git push하여 서버에 업로드, git pull하여 내려받음

commit 뜻

1. 스냅샷된 정보를 담음
2. ID로 고유한 hash코드가 부여됨
3. 누가 작성했는지, 언제 했는지가 기록됨

git add

# 1개 파일 add
git add <file>

# 2개 파일 add
git add <file1> <file2>

# 전체 수정 파일 add
git add .

# 전체 수정 파일 add
git add -A

# 디렉토리 내 전체 파일 add
git add *

working directory의 수정된 사항들을
staging area로 옮기는 행위

git rm --cached <file> 명령어로
staging area의 파일을 working directory로 다시 옮기는것도 가능

.gitignore

git에 포함하고 싶지 않은 파일들을 지정
지정된 파일들은 track되지 않아 git 명령의 대상이 아니게됨

git status

git의 상태를 보여줌

# 짧게보기
git status -s

# 풀버전 (기본)
git status
git status --long

 

git diff

이전 버전과 현재의 수정사항을 비교하여 보여줌
-는 이전 버전을 의미
+는 현재 버전, 수정된 사항을 의미

# 이전 버전과 현재버전을 비교
git diff

# staging area만 확인
git diff --staged

 

git commit

staging area의 변경 사항들은 git repository로 이동시킴

# 메시지와 함께 commit
git commit -m "some message"

# 전체 수정사항을 add 후, commit
git add .
git commit -m "some message"

# 전체 수정사항 add와 commit을 동시에
git commit -am "some message"

commit이야 말로 개발의 history가 됨
기능별, 의미있는 수정/기능 단위로 commit을 진행
커밋의 메시지는 현재형, 동사로 작성하는게 일반적 (init/add/fix)

git log

간단하게 commit 히스토리를 볼 수 있음
위에 있을수록 최신 commit

# 커밋 히스토리 보기
git log

# 간단하게 보기 (해시번호 + 커밋 메시지)
git log --oneline

# 수정사항 함께보기
git log -p

# 텍스트 GUI로 브랜치 함께 보기
git log --oneline --graph all

 

git alias

나만의 명령어를 만들 수 있음

git config --global alias.<이름> "<수행할 액션>"

 

HEAD

각 커밋은 이전 커밋을 가리킨다.
HEAD는 내가 현재 위치한 버전을 가리킨다.
즉, 현재 내가 보고있는 커밋을 가리킨다.

HEAD~1은 직전 버전
HEAD~2은 이전이전 버전
HEAD~3은 이전이전이전 버전
을 의미한다.

git tag

특정한 커밋을 북마크해두고 싶을때 쓰는것
제품을 릴리즈할떄 제품의 버전명을 tag해두는게 일반적

# 버전관리 - semantic versioning
major.minor.fix

# 예시
1.0.1

# 태그 추가
git tag <태그명>

# 특정 커밋 해시코드에 태그 추가
git tag <태그명> <해시코드>

# 특정 커밋 해시코드에 태그를 메시지와 함께 추가
git tag <태그명> <해시코드> -am "메시지"

# 태그 내용 조회
git show <해시코드>

# 태그 찾기, 와일드 카드 사용가능
git tag -l "v1.*" 

# 태그 삭제
git tag -d <태그명>

 

git branch

기능개발 / 버그픽스 등 업무를 진행함에 있어
개발자들 간에 병렬적으로 개발하기 위해
각자의 작업 공간을 나누어 개발을 진행한다.

이때 필요한게 나만의 작업공간 "branch"
특정 기능을 위한 작업공간 "branch"


특정 브랜치에서 git commit을 해나가다가
개발이 완료되면 기준 브랜치 혹은 마스터 브랜치로 병합을 하게되는데,(merge)

이때 그냥 병합하게 되면 브랜치가 지저분해져서
커밋 내역을 합쳐서 새로운 하나의 커밋을 만든 다음 병합하는 경우가 많다. (rebase & merge)

# 로컬의 브랜치 확인
git branch

# 로컬 & 서버 브랜치 확인
git branch --all

# 브랜치 이동 방법, 2가지
git switch <브랜치 이름>
git checkout <브랜치 이름>

# 브랜치 생성 & 이동 방법, 2가지
git switch -C <브랜치 이름>
git checkout -b <브랜치 이름>

# 해시코드를 이용해 브랜치 이동
git checkout <해시코드>

# 현재 브랜치에 merge된 브랜치 확인
git branch --merged

# 현재 브랜치에 merge얀된 브랜치 확인
git branch --no-merged

# 브랜치 삭제
git branch -d <브랜치 이름>

# 브랜치 삭제 & 원격저장소에 브랜치 삭제 반영
git branch -d <브랜치 이름>
git push origin --delete <브랜치 이름>

# 브랜치 이름 변경
git branch --move <이전 브랜치 이름> <새 브랜치 이름>

# 브랜치 이름 변경 & 원격 저장소에 변경된 브랜치 반영
git branch --move <이전 브랜치 이름> <새 브랜치 이름>
git push --set-upstream origin <새 브랜치명>

 

fast forward

흔히 말하는 fast forward란 무엇일까?

과거의 히스토리인 A커밋과

현재의 히스토리인 B커밋이 있을때,

B커밋이 A커밋의 모든 이력을 가지고 있다면

두 커밋이 "fast forward" 관계에 있다고 한다.

(이미지 출처 > 링크)

 

git merge

fast forward merge?
기준 브랜치에서 새로운 브랜치가 생성된 이후에
새 브랜치에서 작업하는 동안
기준 브랜치에 변동사항이 없다면,

기준 브랜치가 가리키고 있는 포인터를
새 브랜치로 변경해준 뒤
새 브랜치를 삭제해주는 방식으로 머지하는 것을 의미한다.

merge하면서도 깔끔하게 브랜치를 관리할수 있다는 장점이 있다.

다르게 생각하면 history에 merge되었다는
이력이 남지않는다는 단점이 존재한다고 볼수도 있다.

merge했을때 기준 브랜치에 변동사항이 없다면
자동으로 fast forward merge가 진행된다.

# feature/a를 master에 merge
git switch master
git merge feature/a
git branch -d feature/a

# fast forward merge 방지
git merge --no-ff <브랜치명>


three way merge?
내가 새 브랜치에서 작업하는동안에
기준 브랜치에 수정사항이 커밋됐다면,
fast forward merge가 불가능하게 된다.

이때는 기준 브랜치와 새 브랜치의 변동 사항을 모두 합쳐서
새로운 merge commit을 만든 후
기준 브랜치에 커밋해야한다.


merge할때 두 브랜치에서 동일한 파일을 수정했을때
"conflict"가 발생한다.
이러한 경우에는 개발자가 직접 conflict를 해결해야한다.

conflict이 발생하면 관련 내용에
자동으로
<<< HEAD
충돌 BRANCH >>>
텍스트가 생성된다.

# merge를 취소하려면
git merge --abort

# merge conflict를 해결 후, 해결 했음을 알리려면
git add <충돌 해결 파일명>
git merge --continue

# merge 후 orig 파일 생성되는것 방지
git config --global mergetool.keepBackup false
git clean -fd


커밋된 사항을 묶어 새로운 하나의 커밋을 만들어
머지하는 방법도 있다.
feature 브랜치의 커밋 히스토리를 합쳐서 깔끔하게
만들기위해 사용된다.

# squash merge
git switch master
git merge --squash <머지할 브랜치>
git commit -m "<메시지>"


git rebase

기준점을 재설정 함으로써
fast forward merge가 가능한 상황을 만들어
브랜치를 깔끔하게 관리하기 위한 수단.

마치 하나의 브랜치에서 작업한듯한 히스토리를
만들 수 있다.

** 로컬레포지토리에서는
자유롭게 rebase를 이용해도 된다.

다만, remote 레포지토리에 반영된 사항에
rebase를 하다가 merge conflict이 발생할 수 있다.

# feature/b를 master에 rebase
git switch feature/b
git rebase master

git switch master
git merge feature/b

# feature/view, feature/view/logic
# logic을 먼저 올려줄것을 요청하는 다른 개발자가 있어서
# feature/view/logic을 master에 merge 해보기
git switch master
git rebase --onto master feature/view feature/view/logic
git merge feature/view/logic


rebase의 또 다른 활용법이 있다.
하나의 feature를 구현함에 있어서도
분명 세부적인 기능들이 나눠진다.

따라서, 로컬에서 개발을 진행할 때
n번에 나눠서 commit을 진행하게 되는데
이때 그냥 push하면 모든 commit기록이
remote 서버로 넘어가기 때문에 브랜치가 지저분해진다.

내 로컬 작업을 간단히 정리해서 push하기위해
아래와 같은 사전작업을 할 수 있다.

# git log로 커밋 이력 확인하기
git log

# 최근 3개의 커밋에 대해 정리하기
git rebase -i @~3

# push하기
git push origin <브랜치명>

위 rebase 작업에서는 2번의 텍스트 에디터 화면을 보게된다.
1. commit 선별하기
p (pick, 사용하기)
| r (reward, 사용하되 커밋 메시지 수정하기)
| s (squash, 사용하되 이전의 커밋에 녹이기)
등의 옵션을 이용할 수 있다.
2. commit 메시지 정리하기
dd명령어를 이용해 원하는 라인을 제외하고 모두 지우거나
새 커밋 메시지를 작성하여
커밋 메시지를 깔끔하게 할 수 있다.
(해당 내용은 다음 포스팅에서 더 상세히 다룸)

cherry pick

다른 브랜치에 작업중인 특정 커밋만,
내 브랜치에 머지하고자할때 사용

# 특정 커밋 가져오기
git cherry-pick <해시코드>
728x90
반응형

'개발, 코딩' 카테고리의 다른 글

객체지향 프로그래밍 핵심 개념  (0) 2022.10.27
git 기본기_백과사전(2)  (0) 2022.10.26
git 협업하기 - 실무편  (0) 2022.10.23
Webpack 구성 이해하기  (0) 2022.10.04
Rest API 정리  (0) 2022.09.29