2025/4/3(木)
■ localhost == 127.0.0.1
Q. 타입 스크립트는 조건문 안에 값이 있으면 참/거짓을 반환하도록 설정되 있는 거야? 좀 더 기본적인 문법 표현은 없어?
A.
네, TypeScript(혹은 JavaScript)에서는 **조건문(if)**에서 값을 사용할 때, 그 값이 **참(true) 또는 거짓(false)**로 평가됩니다. 이 동작은 Truthy와 Falsy 개념에 기반합니다. 다만, 기본적인 문법 표현을 원하신다면, 조건문에 사용할 수 있는 명시적인 비교를 사용할 수도 있습니다.
1. Truthy와 Falsy
JavaScript와 TypeScript에서는 Falsy 값을 제외한 나머지 값들은 모두 Truthy로 간주됩니다. 그래서 조건문 내에서 변수나 값을 직접 사용할 수 있습니다.
Falsy 값: false, 0, "" (빈 문자열), null, undefined, NaN
Truthy 값: 그 외 모든 값은 Truthy로 취급됩니다.
예시:
typescript
コピーする
let userName: string = "John"; // Truthy 값
if (userName) {
console.log("userName은 비어 있지 않아요!"); // 이 구문이 실행됩니다.
}
let emptyString: string = ""; // Falsy 값
if (emptyString) {
console.log("이 구문은 실행되지 않아요.");
} else {
console.log("빈 문자열이므로 else가 실행됩니다."); // 이 구문이 실행됩니다.
}
]
2025/4/4(金)
■ docker volume 삭제 후 갱신 명령어
docker volume ls
docker ps
docker inspect [container ID]:갱신하고 싶은 docker volume name을 찾는다
docker-compose down
docker volume rm [volume name]
docker-compose build : 도커 파일을 수정하면 항상 build를 해줘야 한다.
docker-compose up
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
-- ユーザテーブルの作成
create table user (
id int AUTO_INCREMENT comment 'ID',
name nvarchar(200) comment '名前',
age int comment '年齢',
sex nvarchar(2) comment '性別',
dept_id int comment '部署コード',
primary key (id),
foreign key (dept_id) references department(dept_id)
on update cascade
on delete cascade
) comment='ユーザ情報';
-- 部署テーブルの作成
create table department (
dept_id int AUTO_INCREMENT comment '部署コード',
name nvarchar(20) comment '部署名',
leader nvarchar(20) comment '部署長',
primary key (dept_id)
) comment='部署情報';
※上はDockerによるテーブル定義ファイルである。
version: '3'
services:
mysql-j:
container_name: demo-mysql-j
build: docker/mysql
volumes:
- ./docker/mysql/conf.d:/etc/mysql/conf.d
- ./docker/mysql/initdb.d:/docker-entrypoint-initdb.d //이 파일에 테이블 정의서 있음.
ports:
- 43306:3306
environment:
MYSQL_DATABASE: demo
MYSQL_USER: sa
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-password}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-password}
TZ: Asia/Tokyo
command: --innodb-use-native-aio=0
networks:
eandm-nw-j:
user-api-j:
container_name: demo-user-api-j
build: docker/user-api
volumes:
- ./user-api:/user-api
ports:
- 3001:3000
environment:
TZ: Asia/Tokyo
command: sh -c "npm ci && npm run start:debug"
networks:
eandm-nw-j:
networks:
eandm-nw-j:
external: true
TypeORM에서는 @ManyToOne 등의 관계를 정의해도, 실제 외래키 제약이 데이터베이스에 반영되려면
typeorm의 마이그레이션 기능을 사용하여 반드시 마이그레이션을 생성하고 실행해야 한다.
마이그레이션을 실행하지 않으면, 코드 상의 관계 설정은 있지만 실제 데이터베이스에는 반영되지 않게 됩니다.
// User Entity
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { Department } from './Department';
@Entity('user')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
age: number;
@Column()
sex: string;
@ManyToOne(() => Department, (department) => department.users)
@JoinColumn({ name: 'dept_id' })
dept: Department;
}
// Department Entity
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { User } from './User';
@Entity('department')
export class Department {
@PrimaryGeneratedColumn()
dept_id: number;
@Column()
name: string;
@Column()
leader: string;
@OneToMany(() => User, (user) => user.dept)
users: User[];
}
-
마이그레이션 생성:
npx typeorm migration:generate -n CreateUserAndDepartment
- 마이그레이션 실행:
npx typeorm migration:run
위 명령어들은 TypeORM이 【현재 엔티티와 데이터베이스 간의 차이】를 비교하여 필요한 SQL 쿼리를 생성하고,
해당 SQL을 데이터베이스에 실행하는 과정을 자동으로 처리해줍니다.
migration:run 명령을 실행하면 외래키 제약이 포함된 SQL이 실제 데이터베이스에 반영됩니다.
3. 자동 생성되는 SQL 예시
만약 User 엔티티에서 Department와의 관계를 설정했다면, 마이그레이션을 실행하면
TypeORM은 다음과 같은 SQL을 자동으로 생성합니다:
ALTER TABLE `user`
ADD CONSTRAINT `FK_user_dept_id`
FOREIGN KEY (`dept_id`) REFERENCES `department`(`dept_id`)
ON DELETE CASCADE
ON UPDATE CASCADE;
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Q.마이그레이션이란(타입스트립트의 typeorm을 사용한다는 전제로 설명)
1. 마이그레이션이란?
간단히 말해, 마이그레이션은 데이터베이스 스키마 변경을 관리하는 도구입니다.
예를 들어, 새로운 테이블을 추가하거나 기존 테이블의 컬럼을 변경하는 등의 작업이 있을 때,
마이그레이션 파일(실제로 타입스크립트와 nestjs로 마이그레이션 파일을 만드는 예시 아래에 있음)을 작성하여 이를 관
리하고 실행합니다.
마이그레이션을 통해:
- 데이터베이스의 상태를 추적할 수 있습니다.
- 어떤 변경 사항이 언제 일어났는지 기록할 수 있습니다.
- 여러 명이 작업하는 환경에서도 데이터베이스 구조를 일관되게 유지할 수 있습니다.
마이그레이션(수동) 파일 생성/실행 예시(프로덕트 환경이라는 가정)
// User Entity
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { Department } from './Department';
@Entity('user')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
age: number;
@Column()
sex: string;
@ManyToOne(() => Department, (department) => department.users)
@JoinColumn({ name: 'dept_id' })
dept: Department;
}
// Department Entity
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { User } from './User';
@Entity('department')
export class Department {
@PrimaryGeneratedColumn()
dept_id: number;
@Column()
name: string;
@Column()
leader: string;
@OneToMany(() => User, (user) => user.dept)
users: User[];
}
※上のtypeorm Entityを例にして説明を続ける
NestJS에서 TypeORM을 사용하여 **User**와 Department 엔티티를 데이터베이스에 반
영하려면, 마이그레이션(수동)을 사용해야 합니다.
여기서 마이그레이션은 엔티티 변경 사항을 데이터베이스에 반영할 수 있도록 도와주는 【TypeORM의 기능】입니다.
마이그레이션을 작성하고 실행하는 방법을 단계별로 설명하겠습니다.
(타입스트립트의 typeorm을 사용한다는 전제로 설명)
1. NestJS 프로젝트 설정
먼저, NestJS 프로젝트에서 TypeORM을 설정해야 합니다.
(1) @nestjs/typeorm과 typeorm 패키지 설치
NestJS와 TypeORM을 사용하려면 @nestjs/typeorm과 typeorm 패키지를 설치해야 합니다
npm install @nestjs/typeorm typeorm mysql
여기서 mysql2는 MySQL 데이터베이스를 사용할 경우 필요한 드라이버입니다.
사용 중인 데이터베이스에 맞는 드라이버를 선택하세요 (예: PostgreSQL의 경우 pg 등).
(2) TypeORM 설정 파일 (app.module.ts)
NestJS의 **AppModule**에서 TypeORM을 설정합니다.
TypeOrmModule.forRoot() 메서드를 사용하여 데이터베이스 연결을 설정합니다.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Department } from './entities/department.entity';
import { UserService } from './user.service';
import { DepartmentService } from './department.service';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql', // 사용 중인 DB 종류
host: 'localhost', // DB 서버 호스트
port: 3306, // DB 포트
username: 'root', // DB 사용자
password: 'password', // DB 비밀번호
database: 'test_db', // DB 이름
entities: [User, Department], // 엔티티 등록
synchronize: false, // 자동 동기화 비활성화
logging: true,
}),
TypeOrmModule.forFeature([User, Department]), // 엔티티 등록
],
providers: [UserService, DepartmentService],
})
export class AppModule {}
2. 마이그레이션을 위한 설정
마이그레이션을 사용하려면 ormconfig.json 또는 data-source.ts를 설정해야 합니다.
synchronize: false로 설정하여 자동 DB 스키마 동기화를 끄고, 마이그레이션(수동)을 사용할 준비를 합니다.
(1) data-source.ts 설정 (권장 방법)
NestJS 프로젝트에서 data-source.ts 파일을 생성하여 TypeORM을 설정할 수 있습니다.
import { DataSource } from 'typeorm'; // 이 부분에서 TYPEORM 기능 등록
import { User } from './entities/user.entity';
import { Department } from './entities/department.entity';
export const AppDataSource = new DataSource({
type: 'mysql', // 사용 중인 DB 종류
host: 'localhost', // DB 서버 호스트
port: 3306, // DB 포트
username: 'root', // DB 사용자
password: 'password', // DB 비밀번호
database: 'test_db', // DB 이름
entities: [User, Department], // 엔티티 등록
synchronize: false, // synchronize를 false로 설정하여 자동 동기화 비활성화
logging: true, // SQL 로그 출력
migrations: ['./src/migrations/*.ts'], // 마이그레이션 경로 설정
});
3. 마이그레이션 생성 및 실행
이제 실제로 마이그레이션 파일을 생성하고 실행하는 방법을 살펴보겠습니다.
(1) 마이그레이션 파일 생성
먼저, TypeORM CLI를 사용하여 마이그레이션 파일을 생성합니다.
npx typeorm migration:generate -n CreateUserAndDepartmentTables
이 명령어를 실행하면 src/migrations 폴더에 새로운 마이그레이션 파일이 생성됩니다.
이 파일은 데이터베이스에서 엔티티에 정의된 테이블을 생성하는 SQL 쿼리(CREATE문)를 포함합니다.
※변경된 엔티티와 현재 데이터베이스 구조를 비교하여 변경된 내용을 포함한 마이그레이션 파일을 자동으로 생성
(2) 마이그레이션 파일 확인
src/migrations 폴더에 생성된 마이그레이션 파일을 확인합니다. 예시:
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateUserAndDepartmentTables1618352932357
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE department (
dept_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
leader VARCHAR(255) NOT NULL
) ENGINE=InnoDB;
`);
await queryRunner.query(`
CREATE TABLE user (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
age INT NOT NULL,
sex VARCHAR(10) NOT NULL,
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES department(dept_id) ON DELETE CASCADE
) ENGINE=InnoDB;
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP TABLE IF EXISTS user');
await queryRunner.query('DROP TABLE IF EXISTS department');
}
}
위 파일에서 up() 메서드는 테이블을 생성하고, down() 메서드는 테이블을 삭제하는 작업을 수행합니다.
(3) 마이그레이션 실행 - up() 메서드 실행됨
마이그레이션 파일을 실행하여 데이터베이스에 테이블을 생성합니다.
npx typeorm migration:run
이 명령어는 up() 메서드를 실행하여 실제 데이터베이스에 테이블을 생성합니다.
4. 마이그레이션 롤백- down() 메서드 실행됨
마이그레이션을 롤백하고 싶다면 migration:revert 명령어를 사용합니다.
npx typeorm migration:revert
이 명령어는 down() 메서드를 실행하여 이전 상태로 되돌립니다.
마이그레이션 작업 흐름:
- 엔티티 변경: 새로운 필드를 추가하거나 테이블을 수정합니다.
- 마이그레이션 파일 생성: npx typeorm migration:generate 명령어를 사용하여 마이그레이션 파일을 생성합니다
- 마이그레이션 실행: npx typeorm migration:run 명령어를 사용하여 마이그레이션을 실행하고 변경 사항을 데이터베이스에 반영합니다.
- 롤백: 문제가 발생하면 npx typeorm migration:revert 명령어를 사용하여 마이그레이션을 되돌립니다.
Q. 왜 프로덕트 환경에서는 마이크레이션을 수동으로 관리해야 되는 거야?
개발 환경에서는 synchronize: true 옵션을 통해 TypeORM이 자동으로 데이터베이스 스키마를 변경할 수 있습니다.
하지만 프로덕션 환경에서는 자동 동기화나 자동 마이그레이션 실행을 피해야 하는 이유가 있습니다.
주요 이유들:
- 데이터 손실 위험:
- 자동 동기화(synchronize: true)를 사용할 경우, TypeORM이 엔티티와 데이터베이스를 비교하면서 구조 변경을 시도합니다. 예를 들어, 엔티티가 변경될 때 테이블을 삭제하고 새로 생성하거나 컬럼을 삭제할 수 있습니다. 이런 작업은 기존 데이터 손실을 초래할 수 있습니다. 특히 프로덕션 환경에서 중요한 데이터를 다루고 있을 때, 예기치 않은 데이터 손실을 방지하려면 자동 동기화를 끄는 것이 좋습니다.
- 업데이트와 롤백을 명확하게 관리:
- 프로덕션 환경에서는 업데이트와 롤백을 명확하게 관리하는 것이 중요합니다. 마이그레이션을 사용하면 어떤 변경이 언제 적용됐는지 추적할 수 있으며, 문제가 생겼을 때 롤백을 통해 원래 상태로 복원할 수 있습니다.
- 예를 들어, 신규 기능을 배포하기 전에 마이그레이션을 통해 데이터베이스 구조를 변경하고, 배포 후에 문제가 발생하면 이전 상태로 되돌리는 **rollback**을 쉽게 할 수 있습니다.
- 업데이트 순서 관리:
- 여러 명이 동시에 작업하는 경우, 여러 엔지니어가 데이터베이스 구조를 변경할 수 있습니다. 이때 마이그레이션 파일을 버전 관리하여 동기화된 상태(이 말의 의미는 아래에서 설명을 해 놓음)로 데이터베이스를 변경해야 합니다. 자동화된 시스템이 예기치 않게 구조를 바꿔버리면 다른 팀의 작업과 충돌할 수 있습니다.
- 데이터베이스의 일관성:
- 자동으로 데이터베이스를 업데이트하면, 서버의 상태와 실제 데이터베이스 상태가 일치하지 않는 문제가 발생할 수 있습니다. 마이그레이션을 수동으로 실행하면서 정확하게 각 스텝을 제어하고, 데이터베이스의 일관성을 유지할 수 있습니다.
- 프로덕션 서버에 대한 제어:
- 프로덕션 환경에서는 비즈니스가 중단되는 것을 최소화해야 합니다. 예를 들어, 테이블 구조 변경이나 데이터 마이그레이션 작업 중에 서버 다운타임이나 기능 중단이 발생할 수 있습니다. 따라서 수동으로 마이그레이션을 실행하여 이런 작업들을 예상 가능한 시간에 수행하고, 비즈니스 운영에 미치는 영향을 최소화해야 합니다.
마이그레이션 파일을 버전 관리하여 동기화된 상태로 데이터베이스를 변경한다는 개념은, 데이터베이스 스키마의 변경 사항을 추적하고, 여러 환경에서 동일한 스키마 변경을 적용하는 방법을 의미합니다. 이 과정을 통해 데이터베이스의 상태를 코드처럼 관리하고 변경 이력을 추적할 수 있습니다.
예시를 통해 설명하겠습니다.
1. 기본 상황
처음으로 개발을 시작할 때, User 테이블이 필요하다고 가정해봅시다.
// User 엔티티
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('user')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
2. 마이그레이션 파일 생성 (처음 설정)
이 상태에서 typeorm migration:generate 명령어를 실행하면, 현재의 엔티티 구조를 기존 데이터베이스 스키마와 비교하고, 두 구조의 차이를 마이그레이션 파일로 만들어줍니다. 이 파일은 엔티티 구조에 맞춰 데이터베이스를 최초로 설정하는 SQL 쿼리를 포함하고 있습니다.
이 명령어를 실행하면 생성된 마이그레이션 파일은 대략 이렇게 생겼을 것입니다:
import { MigrationInterface, QueryRunner } from "typeorm";
export class InitialCreateUser1632753404386 implements MigrationInterface {
name = 'InitialCreateUser1632753404386'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "user" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_53b8a3c15f89a85ab8746a8de76" PRIMARY KEY ("id"))`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "user"`);
}
}
이 파일은 up 메서드에 데이터베이스를 변경하는 SQL 쿼리를 담고 있고, down 메서드에는 롤백할 수 있는 SQL 쿼리를 담고 있습니다.
3. 마이그레이션 파일 실행
마이그레이션 파일을 데이터베이스에 반영하려면 typeorm migration:run 명령어를 사용합니다.
4. 변경 사항 (예시: age 컬럼 추가)
이제 User 엔티티에 age 컬럼을 추가하려고 한다고 가정해봅시다.
// User 엔티티 변경
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('user')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column() // 새로 추가된 'age' 컬럼
age: number;
}
엔티티가 변경되었으므로, 다시 마이그레이션 파일을 생성합니다:
npx typeorm migration:generate -n AddAgeToUser
이 명령어는 변경된 엔티티를 기준으로 새로운 마이그레이션 파일을 생성합니다. 생성된 마이그레이션 파일은 대략 이렇게 생겼을 것입니다:
import { MigrationInterface, QueryRunner } from "typeorm";
export class AddAgeToUser1632753404387 implements MigrationInterface {
name = 'AddAgeToUser1632753404387'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "user" ADD "age" integer NOT NULL`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "age"`);
}
}
이 마이그레이션 파일은 user 테이블에 age 컬럼을 추가하는 SQL 쿼리를 담고 있습니다.
5. 마이그레이션 파일 실행 (변경 사항 반영)
변경 사항을 데이터베이스에 반영하려면 다시 typeorm migration:run 명령어를 실행합니다:
이 명령어는 AddAgeToUser 마이그레이션 파일을 실행하여, 데이터베이스에 age 컬럼을 추가합니다.
6. 마이그레이션 파일 버전 관리
마이그레이션 파일들은 버전 관리 시스템(예: Git)을 통해 관리됩니다. 예를 들어, 팀원들이 서로 다른 환경에서 작업할 때, 같은 마이그레이션 파일을 공유하여 동일한 데이터베이스 구조를 유지할 수 있습니다.
Git에 마이그레이션 파일을 커밋하고 푸시하면, 다른 개발자들이 해당 마이그레이션 파일을 migration:run 명령어를 통해 실행하여, 동일한 데이터베이스 구조로 동기화할 수 있습니다.
마이그레이션 파일의 역할
- 버전 관리: 마이그레이션 파일은 데이터베이스 스키마의 변경 사항을 버전처럼 다룹니다. 각 마이그레이션 파일은 특정 시점에서 데이터베이스에 적용된 변경 사항을 기록합니다.
- 동기화된 데이터베이스 상태 유지: 여러 개발자가 작업을 하거나, 여러 환경(개발, 테스트, 프로덕션)에서 데이터베이스를 동기화할 수 있습니다. 모든 팀원이 같은 마이그레이션 파일을 실행하면, 동일한 데이터베이스 구조를 유지할 수 있습니다.
예시 요약
- 최초 생성: typeorm migration:generate -n InitialCreateUser 명령어로 마이그레이션 파일을 생성하고 데이터베이스에 반영.
- 변경 사항 적용: 엔티티를 변경하고, typeorm migration:generate -n AddAgeToUser로 새로운 마이그레이션 파일을 생성하여 데이터베이스에 반영.
- 버전 관리: 마이그레이션 파일을 Git과 같은 버전 관리 시스템에 커밋하여, 팀원들이 동일한 변경 사항을 데이터베이스에 적용하도록 함.
자동 마이그레이션 예시(개발 환경에서만 사용해야 한다.)
개발 환경에서 자동화
- 개발 환경에서는 synchronize: true 옵션을 사용하여 데이터베이스 스키마가 엔티티 클래스에 맞게 자동으로 업데이트되도록 할 수 있습니다. 이 옵션을 활성화하면, TypeORM은 서버가 시작될 때마다 엔티티 변경 사항을 데이터베이스에 자동으로 반영합니다.
예를 들어, synchronize: true로 설정하면 자동으로 테이블을 생성하고, 엔티티의 변경 사항을 실시간으로 반영합니다.
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'test_db',
entities: [User, Department],
synchronize: true, // 엔티티 변경 시 자동으로 데이터베이스를 동기화
});
4월 9일(수)
SQL문은 크게 쿼리(Query)와 명령문(Statements)이 있다.
- 쿼리는 주로 SELECT문을 의미하지만, 일반적으로 SQL문 전체를 지칭할 때 데이터를 조회하거나 조작하는 명령어를 포함합니다.
- 쿼리 이외의 SQL문은 데이터베이스의 구조를 변경하거나 사용자 권한을 관리하거나 트랜잭션을 제어하는 명령어들입니다. DDL, DML, DCL, TCL 명령어들이 이에 해당합니다.
따라서 쿼리 외의 SQL문은 CREATE, INSERT, UPDATE, DELETE, GRANT, COMMIT 같은 SQL 명령어들을 말하는 것입니다.
4월 24일(목)
【git branch 만드는 법】
1. Ubutu 터미널에서 "git clone [HTTP 저장소 주소]"를 입력
2. clone이 된 디렉토리로 이동
3. [code .] 명령어로 VsCode에서 프로젝트 열기
4. 親Branch를 검색
5. " 親Branch以下に新規Branch作成"
6. feature/j-kimu/티켓번호
4월 24일(목)
【git merge】하는 법
우선 현재 나의 brach를 바로 release 저장소에 commit을 해버리면 내가 commit한 소스 코드 이외의 release 저장소에
저장돼 있던 모든 소스 코드가 날라간다.
그렇기 때문에 아래의 순서를 반드시 지켜줘서 release 저장소에 merge 시켜야 한다.
1. git fetch를 통해 해당 프로젝트에 존재하는 모든 repository의 로그 기록을 최신화해야 한다.
-> 새롭게 생성된 branch들이 있다면 branch 목록에 뜰 것이다
-> 만약 매주 마다 release repository가 생성이 되는데 git fetch를 하지 않으면 이전에 생성된 release repository밖에 로그
기록으로 나오지 않게 된다.
2. 새롭게 생성된 branch가 있으면 그것을 선택하고 pull하여 코드를 가져 온다.
3. 다시 나의 branch로 돌아 간다.
4. git merge [새롭게 생성된 branch]
-> 이 과정을 통하여 내 branch에 있는 코드와 2번에서 pull한 코드가 하나의 프로젝트로 merge되어
내 로컬 PC에 있는 나의 branch에 된다.
5. 그리고 나서는 gitLab 홈페이지에 들어가서 "create merge request" 버튼을 눌러서 release repository
에 나의 코드가 반영된 코드를 반영해달라고 한다.
6. 이 이후부터는 나의 brach에서 코드를 commit할 때 자동으로 release repository에 commit이 된다.
2025/5/28(水)
Overview: git 명령어, git 競合エラー(conflict error)이 발생하는 주요 상황과 그 원인과 해결 방법 등등
git fetch vs git pull
💡 핵심 차이 먼저 한 줄 요약!
언제 쓰는지 무엇을 하는지
명령어 | 언제 쓰는지 | 무엇을 하는지 |
git clone | 처음 프로젝트를 가져올 때 | 깃 서버의 코드를 처음부터 복사해옴 |
git pull | [이미] 가져온 프로젝트를 최신으로 업데이트할 때 | 변경된 내용만을 업데이트함 |
👨💻 실전 Git 예시로 다시 보기
상황 1: 처음 프로젝트를 받고 싶을 때
git clone https://github.com/example/my-project.git
- 이 명령어는 처음에 깃허브에 있는 코드를 내 로컬 컴퓨터로 복사해옵니다.
- .git 폴더도 같이 가져오기 때문에, 이후에 git pull, git commit 등도 가능해져요(.git 폴더의 역할에 대해서는 아래에서 추후 설명함)
상황 2: 이미 프로젝트가 있는 상태에서 최신으로 업데이트하고 싶을 때
git pull
- 누군가 프로젝트를 업데이트했을 때, 내 컴퓨터에 있는 버전은 예전 것이죠.
- git pull을 하면, 서버(깃허브)에 있는 최신 변경사항[만]을 로컬 컴퓨터에 업데이트
📌 정리 그림
[GitHub 서버]
│
├── git clone → 내 PC에 복제 (처음 가져옴)
│
[내 PC에 코드 생김]
│
└── git pull → 서버에서 최신 변경사항[만]을 가져와 내 코드 업데이트
git pull 요약
git clone 등의 방법으로 이미 복사한 프로젝트와 깃허브 저장소의 최신 코드를 비교하여 최신 변경사항[만]을 내 코드에
업데이트하여 코드를 최신화한다.
git fetch vs git pull
💡 핵심 차이 먼저 한 줄 요약!
명령어 | 언제 쓰는지 | 무엇을 하는지 |
git fetch | 서버 변경사항을 가져오기만 할 때 | 서버에서 최신 정보를 가져오지만, 내 코드에는 적용하지 않음 |
git pull | 가져오고, 내 코드도 바로 업데이트하고 싶을 때 | 서버에서 변경사항을 가져오고 바로 내 코드에 적용함 |
👨💻 실전 Git 예시로 보기
git fetch origin
- 서버(origin)의 최신 변경사항을 내 로컬 저장소로 가져오기만 합니다.
- 하지만 내 폴더에 있는 파일은 변하지 않아요!
- 변경된 내용이 있는지 비교하거나 확인할 수만 있어요.
🖼️ 비유 그림으로 정리
[GitHub 서버]
│
├── git fetch → 최신 내용만 가져와 "알아만 두기" (변경 안 함)
│
└── git pull → 최신 내용을 가져와 "바로 내 코드에 적용" (자동 병합)
git pull은 사실상 git fetch + git merge 두 동작을 합친 거예요.
✅ 차이 정리 표
명령어 | 가져오긴 하나? | 내 코드에 적용? | 언제 사용? |
git fetch | ✅ | ❌ | 변경 확인만 할 때, 직접 병합하고 싶을 때(아래에서 구체적 설명함) |
git pull | ✅ | ✅ | 바로 최신 상태로 만들고 싶을 때 |
※ 직접 병합
# 1. 서버 변경사항만 가져옴 (변화는 없음)
git fetch origin
# 2. 어떤 변경사항이 있는지 확인
git log HEAD..origin/main
# 또는
git diff origin/main
# 3. 확인 후 병합하고 싶으면 직접 merge(직접 병합)
git merge origin/main
💬 언제 git fetch를 쓰면 좋은가요?
- 여러 명이 협업 중인데, 다른 사람이 뭘 바꿨는지 확인만 하고 싶을 때
- 내가 직접 병합하거나 비교하면서 천천히 작업하고 싶을 때
- 실수로 코드 덮어쓰기 싫을 때 (안전하게 먼저 상태만 확인)
예:
git fetch origin
git diff origin/main # 최신 코드와 내 코드의 차이점을 비교/확인하는 명령어
👉 이렇게 하면, 서버의 최신 코드와 내 코드 차이만 확인할 수 있어요!
Q. git fetch가 변경 사항을 확인할 때 사용한다고 하는데 변경 사항을 구체적인 예시란?
어떤 형태로 변경 사항이 표시되는 지??
A. 결론부터 말을 하자면, 보통 git log, git diff 명령어를 통해서 변경 사항을 확인한다.
✅ 시나리오 예시
💻 상황
- 당신은 main 브랜치로 작업 중입니다.
- 동료가 GitHub의 main 브랜치에 새로운 코드를 커밋했습니다.
- 당신은 아직 git pull을 하지 않았어요.
- 이때 git fetch를 사용하면, 서버의 변경사항만 가져와서 비교할 수 있어요.
🧪 1. git fetch 명령어 실행
git fetch origin // origin에 대해서도 아래에서 추후 설명
- GitHub (origin)의 모든 브랜치 최신 커밋 정보를 내 로컬 저장소로만 가져옴
- 내 작업 디렉터리나 현재 브랜치는 변하지 않음
🧾 2. 변경된 내용 확인하는 명령어들
✅ A. 커밋 메시지(commit message) 기준으로 변경 사항 확
git log HEAD..origin/main
🔍 해석:
- HEAD → 내 현재 브랜치 (로컬)
- origin/main → GitHub에 있는 최신 main 브랜치
- 이 명령어는 내가 아직 적용하지 않은 커밋들을 보여줍니다.
🔧 예시 출력:
commit 7bfa234abcd...
Author: YourTeammate
Date: Tue May 28 10:30:00 2025 +0900
Add user profile image support // commit message
commit 8e9b123abcd...
Author: YourTeammate
Date: Tue May 28 09:45:00 2025 +0900
Fix login bug on Safari // commit message
→ 이런 식으로, 무엇이 추가되었는지 커밋 메시지(commit message) 기준으로 확인할 수 있어요
✅ B. 파일 단위로 변경 사항 확인
git diff origin/main
🔍 해석:
- origin/main 기준으로, 내 코드와 어떤 파일 내용이 다른지 보여줌
🔧 예시 출력:
diff --git a/login.js b/login.js
index e69de29..d95f3ad 100644
--- a/login.js
+++ b/login.js
@@ -0,0 +1,10 @@
+function login() {
+ console.log("로그인 시도");
+}
🧩 한 줄씩 해석하기
1. diff --git a/login.js b/login.js
- Git이 login.js 파일의 차이를 비교하고 있다는 의미입니다.
- a/login.js: 변경 전 파일
- b/login.js: 변경 후 파일
2. index e69de29..d95f3ad 100644
- 커밋 전후의 내부 파일 해시값을 비교하는 Git 내부용 정보입니다.
- e69de29 → 변경 전의 파일 해시
- d95f3ad → 변경 후의 파일 해시
- 100644는 **파일의 권한(permission)**을 의미하며, 일반 텍스트 파일을 뜻합니다.
3. --- a/login.js
4. +++ b/login.js
- 이건 변경된 파일의 전/후 상태를 나타냅니다.
- ---는 이전 상태
- +++는 변경된 후 상태
- 둘 다 login.js이므로 → 이 파일이 수정(또는 생성)되었음을 뜻해요.
5. @@ -0,0 +1,10 @@
이건 변경된 범위를 알려주는 줄이에요.
- -0,0: 원래 파일에는 아무 줄도 없었다는 뜻 (빈 파일이었음)
- +1,10: 지금은 1~10줄까지 생겼다는 뜻 (즉, 내용이 추가됨)
6. +function login() {
7. + console.log("로그인 시도");
8. +}
- +가 붙은 줄은 새로 추가된 줄이에요.
- 즉, 이 diff의 핵심은:
- login.js 파일이 원래 비어 있었는데,
아래의 코드가 새로 추가되었어요!
function login() {
console.log("로그인 시도");
}
📌 핵심 요약
명령어 | 설명 |
git fetch | 원격 저장소의 최신 정보를 가져오기만 함 |
git log HEAD..origin/main | 내 브랜치와 서버 브랜치 사이의 커밋 내역 보기(commit message 보기) |
git diff origin/main | 내 코드와 서버 코드의 파일 차이 보기 |
origin
내 컴퓨터(Git 로컬 저장소)와 연결된 원격 저장소(Remote Repository 를 부르는 별칭
👨💻 예시 상황
git clone https://github.com/example/my-project.git
이 명령을 실행하면:
- 내 컴퓨터에 my-project 폴더가 생기고,
- 그 폴더 안에 .git이라는 Git 설정 폴더도 생김(.git 파일에 대해서는 아래에서 추후 설명 예정)
- 그리고 Git은 https://github.com/example/my-project.git 이 주소에 origin이라는 별칭을 붙임\
✅ 언제 origin을 사용하는가?
명령어 | 설명 |
git fetch origin | GitHub 저장소에서 최신 변경사항을 가져와요 |
git push origin main | 내 컴퓨터의 main 브랜치를 GitHub(origin)에 올려요 |
git pull origin main | GitHub(origin)의 main 브랜치를 가져오고 병합해요 |
origin 외에 별칭도 추가 가능
// orion말고도 upstream이라는 별칭을 추가
git remote add upstream https://github.com/otheruser/their-project.git
gitignore 파일
✅ .gitignore의 핵심 역할 한 줄 요약
Git이 무시해야 할 파일 목록을 알려주는 파일
👀 예시로 직접 보기
예: .gitignore 내용
node_modules/
.env
.DS_Store
*.log
이건 무슨 뜻일까요?
항목 | 설명 |
node_modules/ | Node.js에서 생기는 외부 라이브러리 폴더, 용량 크고 다시 설치 가능하니 무시 |
.env | 비밀번호, API키 등 민감한 정보 들어있는 파일, 보안 때문에 공유 금지 |
.DS_Store | macOS가 자동 생성하는 쓸모없는 숨김 파일, 필요 없음 |
*.log | 에러 기록 파일들 (.log), 매번 바뀌고 중요하지 않음 |
🔍 이 파일이 없다면?
- git status 명령어를 실행하면 .env, node_modules/, 로그 파일 등 쓸모없는 파일도 Git이 추적하려고 해요!
- 실수로 커밋돼서 GitHub에 올라가면 큰일 날 수도 있어요 (비밀번호 유출 등)
※ .gitignore 파일에 등록된 목록인 git status 명령어로도 추적이 안 됨
🎯 .gitignore이 필요한 이유 요약
이유 | 설명 |
💾 용량 줄이기 | 쓸데없이 큰 폴더 (예: node_modules) 무시 |
🔒 보안 유지 | 비밀번호, API Key, JWT 같은 민감 정보 .env 제외 |
🧹 프로젝트 깔끔하게 유지 | 로그, 캐시, 임시파일 등 무시해서 깔끔함 |
👥 협업에 문제 생기지 않게 | 모두가 똑같은 불필요 파일 올리는 걸 방지 |
git status
현재 Git 저장소의 상태를 보여주는 명령어
👉 “지금 내 로컬에서 어떤 파일이 수정되었고, 추적되고 있고, 커밋할 준비가 되었는지”를 한눈에 보여줍니다.
🧭 비유로 쉽게 설명하기: “제출하기 전 상태 확인하기”
당신이 학교에서 나의 과제를 제출하기 전에 가방을 열어 확인하는 장면을 떠올려 보세요.
- 어떤 과제는 이미 끝내서 제출 가능해요 (✅ 커밋할 준비 완료)
- 어떤 과제는 아직 수정 중이에요 (✏️ 수정됐지만 저장은 안 됨)
- 어떤 종이는 새로 생긴 건데 선생님이 아직 몰라요 (❓ Git이 추적하지 않음)
→ 이걸 확인하는 게 바로 git status입니다!
🔧 출력 예시 1: 아무것도 변경 안 했을 때
On branch main
nothing to commit, working tree clean
🔧 출력 예시 2: 파일을 수정했지만 add 안 했을 때
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: app.js
→ app.js를 고치긴 했지만 아직 git add를 안 해서 커밋 준비가 안 됐어요
(참고로 git add를 해주게 되면 ステージの変更が起きてpushの対象になる)
🔧 출력 예시 3: 새 파일을 만들었지만 Git이 모를 때
Untracked files:
(use "git add <file>..." to include in what will be committed)
newfile.txt
→ newfile.txt는 Git이 모르는 새 파일이에요. 아직 추적 대상이 아님
(커밋하려면 git add newfile.txt 해야 해요 .git이 모르는 파일도 git add하면 추적 대상이 됨)
🔧 출력 예시 4: git add까지 했을 때
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: app.js
→ 이제 app.js는 커밋할 준비가 완료됐어요!
( git commit 하면 이 변경이 로컬에 저장된다)
✅ 요약 정리표
상황 | git status에 어떻게 나옴? |
파일을 수정함 | "Changes not staged for commit" |
새 파일을 만들었음 | "Untracked files" |
git add까지 했음 | "Changes to be committed" |
아무 변경 없음 | "nothing to commit, working tree clean" |
🎒 한 줄 요약
git status는 "지금 Git이 내 파일들을 어떻게 보고 있는지" 알려주는 검사 도구
Git 충돌(conflict,競合エラー)
Git 충돌은 같은 파일의 같은 부분을 여러 명이 다르게 수정했을 때 발생하는 문제
👉 Git이 어떤 내용을 기준으로 병합할지 결정을 못 해서 멈추는 현상(이 의미는 아래에서 추가 설명)
💥 사람들이 자주 겪는 충돌 상황 3가지 + 예시
🎯 상황 1. 동시에 같은 줄을 수정한 경우
💻 예시:
- 동료 A는 app.js에서 10번째 줄을 이렇게 고쳤어요
const buttonText = "확인";
**당신(B)**도 똑같은 줄을 이렇게 고쳤어요:
const buttonText = "완료";
둘 다 로컬에서 작업한 뒤, B가 나중에 push하려고 하자…
👉 충돌 발생!
😱 원인:
- Git은 같은 줄에 대해 두 명이 다르게 썼을 때 어떤 게 맞는지 모르기 때문에 멈춰요.
🛡️ 방지책:
- git pull을 먼저 해서 다른 사람의 변경사항을 내 작업 전에 미리 반영하기
- 협업 중인 파일은 파일 단위가 아닌 함수 단위로 나눠서 작업하기
Q. 그냥 제일 나중에 push한 사람의 내용으로 덮어 버리면 되는 거 아냐?
A. 이건 많은 분들이 Git을 쓰면서 한 번쯤 꼭 궁금해하는 부분이에요. 그냥 마지막에 push한 사람이 이기면 되는 거 아냐?라는 생각, 정말 자연스럽고 당연하죠. 그런데도 Git이 자동으로 덮어쓰지 않고, 멈추면서 충돌(conflict)이라고 알려주는 이유는 Git이 "코드를 안전하게" 다루도록 설계되었기 때문이에요.
🔍 왜 Git은 그냥 덮어쓰지 않을까?
🚨 이유 1. 자동으로 덮어쓰면 '조용한 오류'가 생길 수 있어요.
Git이 "무조건 나중 사람 걸로 덮자!"라는 식이면…
❗ 이런 일이 벌어져요:
- 동료 A가 정말 중요한 버그 수정 코드를 추가했는데
- 내가 모르고 그냥 내 코드로 덮어써서 그 코드가 사라짐
- 아무도 모르고 넘어가면 → 버그 다시 발생 😨
👉 이걸 **"조용한 오류(silent bug)"**라고 해요.
Git은 이런 위험한 상황을 막기 위해 일부러 멈추는 거예요
🚨 이유 2. "누가 맞는지"는 Git이 판단할 수 없어요.
Git은 컴퓨터 프로그램일 뿐이에요.
→ 사람이 어떤 변경이 "의도한 것인지" 알 수 없어요.
🎯 예시:
- A가 buttonText를 "확인"으로 수정
- B가 똑같은 줄을 "완료"로 수정
이 상황에서 Git은 이런 생각을 못 해요:
“흠, ‘완료’가 더 좋은 단어 같으니까 이걸로 하자!”
Git은 감정도, 맥락도 없어요.
그냥 "둘 다 다르게 고쳤네? 그럼 난 못 판단하니까 너희가 골라!" 라고 멈추는 거예요.
🤖 Git의 철학: “기계는 판단하지 않아요, 인간이 선택하세요.”
Git은 아주 똑똑한 도구지만,
의도를 추측하거나, 맥락을 파악하거나, 옳고 그름을 판단하지는 않아요.
그래서 Git은 말해요:
"둘 다 고쳤는데 겹쳤어요. 어느 쪽이 맞는지 사람이 정하세요. 난 모름!"
🧭 쉽게 비유해 보기: "선생님이 두 명 있을 때"
- 두 명의 선생님이 같은 시험 문제의 정답을 서로 다르게 적었어요.
- 어떤 게 정답인지 학생은 몰라요.
- 이럴 때, 그냥 한 명의 의견으로 덮어쓰면 틀린 답이 될 수도 있어요.
👉 그래서 두 사람의 의견을 비교하고 조율해서 정답을 정해야 해요.
Git도 똑같아요. "무조건 나중 게 맞다"라고 하면 위험하니까 멈추는 거예요.
🎯 상황 2. 다른 사람이 삭제한 파일을 내가 수정했을 때
💻 예시:
- 동료 A가 login.js 파일을 삭제함 → 그리고 push
- 그런데 나는 그 사실을 모르고 login.js를 수정하고 push하려고 함
👉 Git이 "삭제하자 vs 수정하자"를 판단 못 해서 충돌 발생!
🛡️ 방지책:
- git fetch + git status로 미리 변경 사항을 확인
- 협업할 때 중요한 파일은 삭제 전에 슬랙/메신저 등으로 공유하기
🎯 상황 3. 나만의 변경사항 위에 다른 사람의 변경이 생긴 걸 모를 때
💻 예시:
- 나는 README.md에 문장을 추가하고 저장
- 근데 동료 C도 같은 파일에 다른 문장을 추가함
- 나는 바로 push를 시도 → 에러 발생!
😱 원인:
- 나의 로컬 Git 상태가 최신 상태가 아니었기 때문에 발생
- git push는 서버와 내 로컬이 다르면 "충돌이 날 수 있다"며 거절함
🛡️ 방지책:
- 무조건 git push 전에 git pull origin main을 해줘야 해요!
- 또는 git fetch origin → git merge origin/main을 해서 직접 병합 확인
⚠️ 충돌이 무서운 이유
한번 꼬이면 merge commit, rebase, reset 등 복잡한 작업이 필요해짐
💡 충돌을 방지하는 5가지 습관
습관 | 설명 |
🔄 항상 pull 먼저 하기 | push하기 전에 git pull은 필수! |
🔍 작업 전 fetch 하고 변경사항 보기 | git fetch origin → git diff origin/main |
🧪 작업 전 status 보기 | git status로 추적 안 된 파일, 수정된 파일 확인 |
💬 중복 작업 피하려면 커뮤니케이션 필수 | “나 login.js 맡을게!” 같이 미리 말하기 |
🧹 작업 단위를 잘게 쪼개기 | 한 파일에 여러 명이 동시에 손대지 않게 하기 |
🎒 쉬운 비유로 정리
충돌(conflict)은 마치 두 사람이 동시에 같은 종이에 서로 다른 글을 써서, 어떤 걸 최종본으로 쓸지 헷갈리는 상황이에요.
- 해결 방법은?
- 서로 얘기해서 합치거나,
- 수정된 걸 보면서 적절히 조합하거나!
✅ 결론 요약
- 충돌은 같은 줄을 동시에 수정하거나, 누가 삭제한 파일을 다시 수정했을 때 발생
- Git은 "둘 다 다르게 고쳤는데 뭘 따라야 하지?" 라며 멈춤
- 충돌이 무섭다면 항상 pull 먼저, 상태 확인 먼저, 그리고 커뮤니케이션!
상황3의 보충 설명- 위 내용이 이해가 안 되면 아래 내용이 더 쉬우니 아래 내용으로 이해하자
🎯 상황 3: “내가 코드를 바꾸는 사이, 다른 사람도 같은 파일을 바꿨어요!”
🧑💻 상황 요약:
- 당신은 로컬에서 README.md 파일을 수정했어요.
- 그런데 그 사이에 동료 A도 같은 README.md 파일을 수정해서 먼저 push했어요.
- 당신은 그걸 모른 채, 그냥 git push 했는데...
👉 충돌이 발생합니다!
🧪 실제 시나리오 예제로 보기
README.md 내용:
# 프로젝트 소개
이 프로젝트는 멋진 웹 앱입니다.
2️⃣ 동료 A의 작업
동료 A는 README.md를 이렇게 수정하고 커밋 + 푸시함:
# 프로젝트 소개
이 프로젝트는 멋진 웹 앱입니다
. ## 설치 방법
npm install
A는 이걸 GitHub에 먼저 push했어요.
3️⃣ 당신(B)의 작업
당신은 그 사실을 모른 채, 로컬에서 똑같은 README.md 파일을 이렇게 수정했어요:
# 프로젝트 소개
이 프로젝트는 멋진 웹 앱입니다.
## 사용 기술
React, Node.js
그리고 당신은 이렇게 했어요:
git add README.md git commit -m "사용 기술 추가"
git push
💥 그런데 push 하자마자 이런 에러가 뜹니다!
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'origin'
hint: "Updates were rejected because the remote contains work that you do not have locally.
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
👉 즉, 동료 A가 먼저 서버를 바꿨는데 너는 그걸 모르고 덮으려 해서 충돌이 난 거야!
🪛 한 줄씩 해석
1. ! [rejected] main -> main (fetch first)
❌ "main 브랜치에서 origin의 main 브랜치로 push를 시도했지만, **거부(rejected)**당했습니다."
- (fetch first)는:
→ 먼저 fetch를 해 와야 한다는 의미예요.
2. error: failed to push some refs to 'origin'
❌ "origin이라는 원격 저장소로 push 하려던 일부 참조(refs)가 실패했습니다."
- ref는 보통 브랜치를 의미해요 (여기선 main 브랜치)
3. hint: Updates were rejected because the remote contains work that you do
💡 "거부된 이유는, 원격 저장소에 당신이 가지고 있지 않은 새로운 변경사항이 있기 때문입니다."
- 즉, 다른 사람이 먼저 원격에 push해서, 내 로컬보다 최신인 상태예요.
4. hint: not have locally. This is usually caused by another repository pushing
💡 "그 변경 사항은 로컬에 없기 때문에 생겼고, 보통은 다른 누군가가 먼저 push했을 때 이런 일이 생깁니다."
5. hint: to the same ref. You may want to first integrate the remote changes
💡 "같은 브랜치(ref)에 여러 사람이 작업하다가 생긴 문제고,
먼저 원격 변경 사항을 받아와서 합치는 게 좋습니다."
6. hint: (e.g., 'git pull ...') before pushing again.
💡 "예: git pull 명령어로 원격 변경사항을 받아온 뒤 다시 push 해보세요."
📌 요약하면
지금 push 하려는 브랜치(main)가, 원격 저장소(origin/main)보다 뒤처진 상태라서 Git이 막은 거예요.
다른 사람이 이미 먼저 변경한 걸 올렸기 때문에,
당신은 그걸 먼저 받아서 병합하거나 해결한 다음에만 push할 수 있어요.
🔧 왜 충돌이 생겼을까?
- Git 서버에는 A의 내용이 먼저 들어감 (## 설치 방법)
- 당신 로컬에는 그 내용이 없음 (당신은 ## 사용 기술만 있음)
- Git은 두 내용을 자동으로 합치지 못함, 그래서 “너 직접 고쳐봐”라고 하는 거예요.
✅ 해결 방법
git pull origin main
→ 그러면 A의 내용이 당신의 로컬로 들어오고, Git이 자동 병합을 시도해요.
만약 같은 줄을 바꾸지 않았다면 자동 병합 성공!
하지만 같은 줄을 바꿨다면?
→ 수동으로 충돌 해결해야 해요.
🎒 쉬운 비유: "친구가 노트를 먼저 바꿨는데, 나도 몰래 바꾼 상황"
- A가 팀 프로젝트 노트에 “설치 방법”을 추가했어요.
- 그런데 나는 그걸 모른 채 “사용 기술”을 적어버림.
- 선생님이 “둘이 서로 다른 걸 동시에 고쳤네? 누가 맞는 거야?”라고 멈추는 거예요.
→ 그래서 둘이 얘기해서 내용을 합치거나 조정해야 해요.
✅ 한 줄 요약
“충돌 상황 3”은 서버 내용이 바뀐 걸 모르고 내 로컬 내용을 push하려다 생기는 충돌이에요.
항상 git push 전에 git pull을 먼저 해야 안전해요!
서브모듈(submodule)이란
서브모듈은 "Git 저장소 안에 또 다른 Git 저장소"예요.
마치 한 프로젝트 안에 또 다른 독립된 프로젝트가 들어있는 것처럼요.
🎒 쉬운 비유: 학교 팀플 노트 속 '다른 조의 노트'
- 당신이 어떤 큰 팀플(프로젝트)을 하고 있다고 해요.
- 그런데 중간에 다른 조가 만든 문서가 너무 좋아서, 그걸 가져다 쓰기로 했어요.
- 근데 그 조의 문서는 계속 업데이트되니까, 그냥 복사해서 붙여넣으면 안 돼요.
- 그래서 당신은 그 조의 노트를 **링크(연결)**해서, 최신 버전이 나오면 쉽게 가져올 수 있게 해놨어요.
→ 바로 이 연결된 외부 문서가 서브모듈이에요!
🧑💻 실제 개발 예시
당신이 개발 중인 프로젝트 구조가 이렇다고 해요
my-app/
├── src/
├── public/
├── resource/
│ └── node/
│ └── projects/ ← 서브모듈!
├── package.json
└── .gitmodules
여기서 projects/ 폴더는 그냥 폴더가 아니라,
다른 Git 저장소를 연결한 서브모듈이에요.
즉, 이 폴더는 독립된 저장소이고,
내가 직접 컨트롤하지 않아도, 필요할 때 최신 버전으로 업데이트 받을 수 있는 외부 저장소인 거예요.
1. 🔁 공통 모듈 공유
회사에서 여러 프로젝트가 있는데, 모두 같은 UI 컴포넌트 라이브러리를 사용한다면?
→ 그 UI 라이브러리를 하나의 Git 저장소로 만들어서
→ 각 프로젝트가 서브모듈로 연결하면,
→ 한 곳만 업데이트해도 모든 프로젝트가 최신 상태로 유지 가능!
2. ✅ 독립적인 관리
- 서브모듈은 별도 커밋 히스토리를 가짐
- 메인 프로젝트와는 따로 버전 관리됨
- 필요할 때만 pull해서 최신화 가능
📘 서브모듈 관련 Git 명령어
명령어 | 설명 |
git submodule add <url> <경로> | 서브모듈 추가 |
git submodule init | 서브모듈 설정 초기화 |
git submodule update | 서브모듈을 최신 커밋으로 업데이트 |
git submodule update --remote | 서브모듈에서 최신 커밋을 가져옴 (pull 느낌) |
git clone --recurse-submodules <URL> | 클론하면서 서브모듈까지 함께 복제 |
🧾 .gitmodules 파일은 뭐예요?
서브모듈을 쓰면 자동으로 .gitmodules 파일이 생겨요.
예시:
[submodule "resource/node/projects"]
path = resource/node/projects
url = https://github.com/some-org/projects.git
→ 이 파일은 "이 위치에 이 저장소를 서브모듈로 연결해놨어요!"라는 기록입니다.
🧩 요약
🔹 서브모듈은 내 Git 프로젝트 안에 또 다른 Git 프로젝트를 연결하는 기능
🔹 독립된 코드지만, 내 프로젝트 안에서 필요할 때 최신 버전으로 가져올 수 있음
🔹 주로 여러 프로젝트가 공유해야 할 코드(공통 라이브러리, 외부 도구 등)에 사용
📁 .git 폴더란?
🧠 비유: 노트 정리 앱 + 자동 저장 시스템
상상해보세요:
- 당신은 종이 노트에 글을 쓰고 있어요.
- 그런데 그 노트에는 자동 저장 시스템이 달려 있어서,
당신이 어떤 내용을 적었는지, 언제 바꿨는지, 어떤 페이지를 수정했는지를 전부 기록해요.
이 자동 저장 시스템이 설치된 공간이 바로 .git 폴더예요!
🧾 좀 더 기술적으로 설명하면?
.git 폴더 안에는 이런 정보들이 들어 있어요
구성요소 | 역할 |
HEAD | 현재 어떤 브랜치에서 작업 중인지 기억 |
config | 저장소의 환경 설정 (사용자 정보, origin 주소 등) |
refs/ | 각 브랜치, 태그 등의 커밋 위치를 기억 |
objects/ | 지금까지 만든 모든 커밋, 파일, 변경 기록의 데이터 저장소 |
logs/ | 커밋을 언제, 어떤 명령으로 했는지 기록한 일지 |
즉, Git이 모든 과거 기록, 브랜치, 변경 내역, 원격 연결 정보 등을 기억하고 있는 공간이 바로 .git 폴더예요
🧑💻 실제 개발 예시
- 당신이 새 폴더를 만들었어요:
mkdir my-project
cd my-project
git init // 이 순간 my-project에 대한 모든 추적이 시작된다.
이 명령어를 치면, my-project/.git 폴더가 자동으로 생겨요!
그 다음부터는 git add, git commit, git status 등 Git 명령어가 작동해요.
만약 .git 폴더를 삭제하면?
이 폴더는 더 이상 Git 저장소가 아니게 되기에 아무리 git commit을 입력해도 작동하지 않는다.
🧩 Git clone 할 때 .git 폴더도 같이 오는 이유
git clone을 하면 단순히 코드만 복사해 오는 게 아니라,
그 프로젝트의 .git 폴더도 같이 가져와요.
그래서 클론 받은 후에도 마음껏:
- git status로 상태 확인하고
- git commit으로 변경 기록 남기고
- git pull로 원격에서 최신 내용 가져오고
- git push로 내 작업을 올릴 수 있어요!
✅ 정리하면
.git 폴더 | Git이 내부적으로 모든 걸 기억하고 관리하는 공간 |
안에 든 것들 | 커밋 기록, 브랜치 정보, 원격 저장소 주소 등 |
없으면? | Git이 이 프로젝트를 "저장소"라고 인식하지 못함 |
clone하면? | 이 폴더까지 통째로 복사됨 = Git 사용 가능해짐 |
궁금하다면 .git 폴더 안을 실제로 들여다보는 것도 재밌어요!
터미널에서 ls -a 명령어로 보이게 할 수 있어요
// git fetch 후 git diff ~~~~의 출력 예시
diff --git a/login.js b/login.js
index e69de29..d95f3ad 100644
--- a/login.js
+++ b/login.js
@@ -0,0 +1,10 @@
+function login() {
+ console.log("로그인 시도");
+}
🧩 한 줄씩 해석하기
1. diff --git a/login.js b/login.js
- Git이 login.js 파일의 차이를 비교하고 있다는 의미입니다.
- a/login.js: 변경 전 파일
- b/login.js: 변경 후 파일
2. index e69de29..d95f3ad 100644
- 커밋 전후의 내부 파일 해시값을 비교하는 Git 내부용 정보입니다.
- e69de29 → 변경 전의 파일 해시
- d95f3ad → 변경 후의 파일 해시
- 100644는 **파일의 권한(permission)**을 의미하며, 일반 텍스트 파일을 뜻합니다.
3. --- a/login.js
4. +++ b/login.js
- 이건 변경된 파일의 전/후 상태를 나타냅니다.
- ---는 이전 상태
- +++는 변경된 후 상태
- 둘 다 login.js이므로 → 이 파일이 수정(또는 생성)되었음을 뜻해요.
5. @@ -0,0 +1,10 @@
이건 변경된 범위를 알려주는 줄이에요.
- -0,0: 원래 파일에는 아무 줄도 없었다는 뜻 (빈 파일이었음)
- +1,10: 지금은 1~10줄까지 생겼다는 뜻 (즉, 내용이 추가됨)
6. +function login() {
7. + console.log("로그인 시도");
8. +}
- +가 붙은 줄은 새로 추가된 줄이에요.
- 즉, 이 diff의 핵심은:
- login.js 파일이 원래 비어 있었는데,
아래의 코드가 새로 추가되었어요!
function login() {
console.log("로그인 시도");
}
'CSあるある' 카테고리의 다른 글
JOIN 테이블이 만들어 지는 순서 (0) | 2025.02.09 |
---|---|
JOIN 테이블에 대한 UPDATE문 동작 원리 (0) | 2025.02.09 |
SQL은 tuple의 중복을 허락한다! (0) | 2025.02.05 |
constraints(Feat. implicit constraints,explicit constraints) (0) | 2025.02.05 |
DB에서 NULL은 되도록 사용하지 말자 (1) | 2025.02.05 |