반응형

1. 할인 정책의 변경으로 인한 소스 변경

public class OrderServiceImpl implements OrderService {
	// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
	private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
	...
}

 

문제점
- DIP 위반: 

주문 서비스 클라이언트 OrderServiceImpl는 DiscountPolicy 인터페이스 뿐만 아니라 구현 클래스에도 의존함.
      인터페이스 : DiscountPolicy
      구현 클래스 : FixDiscountPolicy , RateDiscountPolicy
- OCP 위반 :

기능을 확장해서 변경하면, 클라이언트 코드에 영향을 줌.

해결 방안 : 

누군가가 클라이언트인 OrderServiceImpl 에 DiscountPolicy 의 구현 객체를 대신 생성하고 주입해주어야 함.


- 관심사의 분리
애플리케이션을 하나의 공연이라 생각해보자. 각각의 인터페이스를 배역(배우 역할)이라 생각하자.

실제 배역 맞는 배우를 선택하는 것은 마치 로미오 역할(인터페이스)을 하는 레오나르도 디카프리오(구현체, 배우)가

줄리엣 역할(인터페이스)을 하는 여자 주인공(구현체, 배우)을 직접 초빙하는 것과 같다.

디카프리오는 공연도 해야하고 동시에 여자 주인공도 공연에 직접 초빙해야 하는 다양한 책임을 가지고 있다.
배우는 본인의 역할인 배역을 수행하는 것에만 집중해야 한다.
디카프리오는 어떤 여자 주인공이 선택되더라도 똑같이 공연을 할 수 있어야 한다.
공연을 구성하고, 담당 배우를 섭외하고, 역할에 맞는 배우를 지정하는 책임을 담당하는 별도의 공연 기획자가 나올시점이다.
공연 기획자를 만들고, 배우와 공연 기획자의 책임을 확실히 분리하자.

 

 

2. 기획자

애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스 생성

package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class AppConfig {
	public MemberService memberService() {
		return new MemberServiceImpl(memberRepository());
	}
	
	public OrderService orderService() {
		return new OrderServiceImpl(memberRepository(),discountPolicy());
	}
    
	public MemberRepository memberRepository() {
		return new MemoryMemberRepository();
	}

	public DiscountPolicy discountPolicy() {
		return new FixDiscountPolicy();
	}
}

 

 

문제가 있었던 회원 서비스 구현체 수정

package hello.core.member;

public class MemberServiceImpl implements MemberService {
	// private final MemberRepository memberRepository = new MemoryMemberRepository();
	private final MemberRepository memberRepository;
    
	// 생성자로 주입
	public MemberServiceImpl(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}
    
	public void join(Member member) {
		memberRepository.save(member);
	}
    
	public Member findMember(Long memberId) {
		return memberRepository.findById(memberId);
	}
}

 

문제가 있었던 회원 서비스 클라이언트

package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;

public class MemberApp {
	public static void main(String[] args) {
		// MemberService memberService = new MemberServiceImpl();
		// 기획자 인스턴스 생성
		AppConfig appConfig = new AppConfig();
		// 실행 시 기획자의 인스턴스로 동적 회원 서비스 생성
		MemberService memberService = appConfig.memberService();
		Member member = new Member(1L, "memberA", Grade.VIP);
		memberService.join(member);
		Member findMember = memberService.findMember(1L);
		System.out.println("new member = " + member.getName());
		System.out.println("find Member = " + findMember.getName());
	}
}

 

 

문제가 있었던 주문 서비스 구현체 수정

package hello.core.order;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;

public class OrderServiceImpl implements OrderService {
	// private final MemberRepository memberRepository = new MemoryMemberRepository();
	// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
	private final MemberRepository memberRepository;
	private final DiscountPolicy discountPolicy;

	// 생성자로 주입
	public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
		this.memberRepository = memberRepository;
		this.discountPolicy = discountPolicy;
	}

	@Override
	public Order createOrder(Long memberId, String itemName, int itemPrice) {
		Member member = memberRepository.findById(memberId);
		int discountPrice = discountPolicy.discount(member, itemPrice);
		return new Order(memberId, itemName, itemPrice, discountPrice);
	}
}

 

문제가 있었던 주문 서비스 클라이언트 

package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.order.Order;
import hello.core.order.OrderService;

public class OrderApp {
	public static void main(String[] args) {
		// MemberService memberService = new MemberServiceImpl();
		// OrderService orderService = new OrderServiceImpl();
		// 기획자 인스턴스 생성
		AppConfig appConfig = new AppConfig();
		// 실행 시 기획자의 인스턴스로 동적 회원 서비스 생성
		MemberService memberService = appConfig.memberService();
		// 실행 시 기획자의 인스턴스로 동적 주문 서비스 생성
		OrderService orderService = appConfig.orderService();
        
		long memberId = 1L;
		Member member = new Member(memberId, "memberA", Grade.VIP);
		memberService.join(member);
		Order order = orderService.createOrder(memberId, "itemA", 10000);
		System.out.println("order = " + order);
	}
}

정리

AppConfig를 통해서 관심사를 확실하게 분리했다.
AppConfig는 공연 기획자다.
AppConfig는 구체 클래스를 선택하여 애플리케이션이 어떻게 동작해야 할지 전체 구성을 책임진다.
OrderServiceImpl, MemberServiceImpl 은 기능을 실행하는 책임만 지면 된다.

 

3. IoC, DI, 그리고 컨테이너
제어의 역전 IoC(Inversion of Control)
- 프로그램의 제어 흐름은 AppConfig가 가져간다. 구현 객체는 자신의 로직을 실행하는 역할만 담당한다. OrderServiceImpl 은 필요한 인터페이스들을 호출하지만 어떤 구현 객체들이 실행될지 모른다.

- 프로그램에 대한 제어 흐름에 대한 권한은 모두 AppConfig가 가지고 있다. 심지어 OrderServiceImpl도 AppConfig가 생성한다. 그리고 AppConfig는 OrderServiceImpl 이 아닌 OrderService 인터페이스의 다른 구현 객체를 생성하고 실행할 수 도 있다. 그런 사실도 모른체 OrderServiceImpl 은 묵묵히 자신의 로직을 실행할 뿐이다. 이렇듯 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)이라 한다.

 

의존관계 주입 DI(Dependency Injection)

- OrderServiceImpl 은 DiscountPolicy 인터페이스에 의존한다. 실제 어떤 구현 객체가 사용될지는 모른다.

- 애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결 되는 것을 의존관계 주입이라 한다.
- 객체 인스턴스를 생성하고, 그 참조값을 전달해서 연결된다.
- 의존관계 주입을 사용하면 클라이언트 코드를 변경하지 않고, 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있다.

 

 

IoC 컨테이너, DI 컨테이너
- AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IoC 컨테이너 또는 DI 컨테이너라 한다. 의존관계 주입에 초점을 맞추어 최근에는 주로 DI 컨테이너라 한다. 또는 어샘블러, 오브젝트 팩토리 등으로 불리기도 한다.

 

 

반응형
반응형

Eclipse(STS)로 세팅 하는 방법

 

1. 사전 준비

- JAVA11 설치 (https://www.oracle.com/kr/java/technologies/javase/jdk11-archive-downloads.html)

- STS설치 (https://spring.io/tools)

자바 11 설치

 

STS 설치

 

2. 스프링 부트 스타터 사이트에서 스프링 프로젝트 생성 (https://start.spring.io)

 

3. STS 설정

- 2.에서 GENERATE한 스프링 프로젝트 원하는 경로에 core.zip 압축 해제

- STS 다운로드 후 압축 해제 → contents.zip 압축 해제 → sts-4.17.2.RELEASE 폴더의 SpringToolSuite4.exe 실행 → 위의 core.zip 압축해제 한 곳으로 Workspace 지정

 

 

4. 순수 자바로 가상의 요구사항 개발

 

4-1) 회원 도메인 설계

회원 저장소에 대한 요구 사항이 명확하지 않아 인터페이스와 구현체를 분리

회원 서비스에 대한 역할도 인터페이스와 구현체를 분리

클라이언트에서 MemberServiceImpl로 실행 로직 구현

 

※ 생각해보기

회원 서비스 구현체

package hello.core.member;

public class MemberServiceImpl implements MemberService {
	// 생각해볼 포인트
	private final MemberRepository memberRepository = new MemoryMemberRepository();
	
	public void join(Member member) {
		memberRepository.save(member);
	}
	public Member findMember(Long memberId) {
		return memberRepository.findById(memberId);
	}
}

 

클라이언트

package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
public class MemberApp {
	public static void main(String[] args) {
		// 생각해볼 포인트
		MemberService memberService = new MemberServiceImpl();
		Member member = new Member(1L, "memberA", Grade.VIP);
		memberService.join(member);
		Member findMember = memberService.findMember(1L);
		System.out.println("new member = " + member.getName());
		System.out.println("find Member = " + findMember.getName());
	}
}

문제점
다른 저장소로 변경할 때 OCP 원칙을 잘 준수하지 못했음.
DIP를 잘 지키고 있지 못함.
의존관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제점이 있음

 

 

4-2) 주문 도메인 설계

회원 저장소에 대한 요구 사항이 명확하지 않아 인터페이스와 구현체를 분리

할인 정책에 대한 요구 사항이 명확하지 않아 인터페이스와 구현체를 분리

회원 서비스에 대한 역할도 인터페이스와 구현체를 분리

클라이언트에서 MemberServiceImpl 와 OrderServiceImpl로 실행 로직 구현

 

※ 생각해보기

주문 서비스 구현체

package hello.core.order;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;

public class OrderServiceImpl implements OrderService {
	// 생각해볼 포인트
	private final MemberRepository memberRepository = new MemoryMemberRepository();
	private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

	@Override
	public Order createOrder(Long memberId, String itemName, int itemPrice) {
		Member member = memberRepository.findById(memberId);
		int discountPrice = discountPolicy.discount(member, itemPrice);
		return new Order(memberId, itemName, itemPrice, discountPrice);
	}
}

 

클라이언트

package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.order.Order;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class OrderApp {
	public static void main(String[] args) {
		// 생각해볼 포인트
		MemberService memberService = new MemberServiceImpl();
		OrderService orderService = new OrderServiceImpl();
		long memberId = 1L;
		Member member = new Member(memberId, "memberA", Grade.VIP);
		memberService.join(member);
		Order order = orderService.createOrder(memberId, "itemA", 10000);
		System.out.println("order = " + order);
	}
}

회원 도메인에서와 마찬가지로 같은 문제점 발생

반응형
반응형

1. 객체지향의 특징 4가지
추상화 : 객체들의 공통적인 특징(기능, 속성)을 도출하는것
캡슐화 : 실제 구현되는 부분을 외부에 드러나지않도록 은닉
상속성 : 하나의 클래스가 가진 특징(함수,데이터)을 다른 클래스가 그대로 물려받는것
다형성 : 다른 방법으로 동작하는 함수를 같은 이름으로 호출하는것, 객체지향의 꽃, 오버로딩 / 오버라이딩

- 다형성
역할(인터페이스)과 구현(인터페이스를 구현한 객체)으로 세상을 구분

예시1)
자동차 역할을 K3, 아반떼, 테슬라로 구현하더라도 운전자는 운전자 역할이 가능하다.
운전자를 클라이언트라고 했을 때, 자동차 역할을 그대로 사용할 수 있다.
자동차의 역할과 구현을 구분한 이유는 운전자(클라이언트)에 영향을 주지 않고 새로운 자동차를 무한히 출시 할 수 있음.


예시2)
로미오 역할을 구현한 장동건, 원빈 / 줄리엣 역할을 구현한 김태희, 송혜교 
배역만 만들어두고, 배우는 언제든지 유연하게 변경할 수 있다.
로미오 역할, 줄리엣 역할을 누가 하든 서로에게 영향을 끼치지 않음.

※ 역할과 구현을 구분하면 세상이 단순해지고, 유연해지며 변경도 편리해짐.
: 클라이언트는 대상의 역할(인터페이스)만 알면됨.
: 클라이언트는 구현 대상의 내부구조를 몰라도됨.
: 클라이언트는 구현 대상의 내부구조가 변경되어도 영향을 받지 않음.
: 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않음.


2. Java의 다형성
역할 = 인터페이스, 구현 = 인터페이스를 구현한 클래스, 구현 객체
객체를 설계 할 때 역할과 구현을 명확히 분리
객체 설계시 역할(인터페이스)을 먼저 부여하고, 그 역할을 수행하는 구현 객체 만들기
(상속도 다형성이 가능하지만 인터페이스로 만드는것이 나음)

 

예시)
다형성으로 인터페이스를 구현한 객체를 실행 시점에서 유연하게 변경이 가능하다.

public class MemberService{
	// 인터페이스를 참조 변수로 선언
	// 인터페이스를 오버라딩하여 구현한 구현 객체 중 원하는 구현 객체로 생성자 함수를 클라이언트가 호출
	private MemberRepository memberRepository = new MemoryMemberRepository(); // 기존 코드
	private MemberRepository memberRepository = new JdbcMemberRepository(); // 변경 코드
}

 

※ 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있음.

 

- 다형성의 본질을 이해하려면 ?

객체는 객체끼리 협력 관계이다.
클라이언트 : 요청, 서버 : 응답
혼자있는 객체는 없음. 수 많은 객체 클라이언트와 객체 서버는 서로 협력관계를 가짐
클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경 할 수 있다.



3. 좋은 객체 지향 설계의 5가지 원칙(SOLID)
- SRP 단일 책임 원칙 : 한 클래스는 하나의 책임만 가져야한다.
변경이 있을때 파급효과가 적으면 단일 책임 원칙을 잘 따른것 (객체의 생성과 사용을 분리)
※ 책임의 범위를 잘 조절해야함


- OCP 개방 폐쇄 원칙 : 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야한다. (다형성)
역할과 구현을 분리, 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현하는것.

※ 생각해보기

public class MemberService{
	private MemberRepository memberRepository = new MemoryMemberRepository(); // 기존 코드
	private MemberRepository memberRepository = new JdbcMemberRepository(); // 변경 코드
}

클라이언트가 구현 클래스를 직접 선택하고있으니 OCP를 지키지 못함.
구현 객체를 변경하려면 클라이언트 코드를 변경해야함.
다형성으로 서비스를 구현했지만 OCP를 지킬 수 없음.
※ 해결방법 : 객체를 생성하고 연관관계를 맺어주는 별도의 조립, 설정자가 필요함(스프링 컨테이너가 해줌 DI, IoC 컨테이너)


- LSP 리스코프 치환 원칙 : 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다.
다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 함. 컴파일은 잘 되더라도 규약을 위배하면 LSP 위반.
자동차 인터페이스의 엑셀은 앞으로가라는 기능, 뒤로가게 구현하게되면 LSP 위반, 느리더라도 앞으로가야함.


- ISP 인터페이스 분리 원칙 : 특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다.
자동차 인터페이스 -> 운전 인터페이스, 정비 인터페이스로 분리
사용자 클라이언트 -> 운전자 클라이언트, 정비사 클라이언트로 분리
정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않음
인터페이스가 명확해지고, 대체 가능성이 높아짐


- DIP 의존 관계 역전 원칙 : 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다.
스프링의 의존성 주입은 이 원칙을 따르는 방법중에 하나.
클라이언트 코드가 구현 클래스를 바라보지 말고 인터페이스를 바라보라는 뜻.
역할에 의존해야지 구현에 의존하지 말라는것.
의존 = 내가 그 코드를 안다.

MemberService 클라이언트는 구현 객체를 알고있음(MemoryMemberRepository)

public class MemberService{
	private MemberRepository memberRepository = new MemoryMemberRepository();
}


클라이언트가 구현객체를 알고있는 것도 DIP 위반



4. 스프링과 객체 지향
스프링은 다형성을 극대화하여 이용할 수 있게 도와줌.
IoC, DI는 다형성을 활용해서 역할과 구현을 편리하게 다룰수 있도록 지원.
스프링을 사용하면 레고 블럭을 조립하듯, 공연무대의 배우를 선택하듯 구현을 편리하게 변경 할 수 있음.

 

- 스프링은 다음 기술로 다형성 + OCP, DIP를 가능하게 지원
DI(Dependency Injection) : 의존관계, 의존성 주입
DI 컨테이너 제공 : 자바 객체들을 컨테이너 안에 넣어두고 의존관계를 서로 연결해주고 주입해주는 기능을 제공
-> 클라이언트 코드의 변경없이 기능 확장 
-> 쉽게 부품 교체 하듯 개발 가능

※ 공연을 설계하듯이 배역만 만들어두고, 배우는 언제든지 유연하게 변경할 수 있도록 만드는것이 좋은 객체 지향 설계이다.

반응형
반응형

1. 왜 GIT을 써야하는가

2. GIT의 구조 및 명령어

3. GIT 사용의 예시

4. GIT 실습

 

4. GIT 실습

4.1 Main - Develop - Feature 브랜치 전략

 Main branch

- 배포 브랜치로 서비스 1.0.0이 출시되기 전까지 건들일 없는 브랜치

 -직접적인 PUSH 절대 불가

 Develop branch

- Feature Branch의 병합 브랜치로 기능이 개발된 브랜치를 병합하여 배포하기 위한 브랜치

 - 팀원들이 만든 기능은 이브랜치로만 머지

 Feature Branch

- 기능 개발 브랜치로 개발자 로컬 저장소에서 관리, 새로운 기능을 구현하는 브랜치

- 브랜치명은 Feat/기능 이름 으로 작업 이후 원격저장소로 Push하여 PR함

- Develop으로 머지

- PR된 Remote Feature 브랜치는 삭제

 

 

4.2 기본 세팅 (PL)

① github에서 새 래포지토리 생성

② 로컬에서 develop branch 생성

 - git branch develop

 - git push --set-upstream origin develop

③ github에서 기본 베이스 변경

 - github > settings > branches > main으로 되어있는것을 develop으로 업데이트

④ Access > Collaborators에서 팀원들 추가

 

 

4.3 PR까지 작업 (팀원)

① github에서 이슈 오픈

 -  github > Issue > New Issue > 내용 작성(Assignees: 작업자, Label : 이슈 분리를 위한 태그) > Submit new Issue

② 로컬에서 원격 develop과 로컬 develop 일치 여부 확인

 - git fetch

 - origin/develop(원격 레포지토리)과 develop(로컬)이 일치 하지 않을 경우

 - git pull origin develop

 - 원격의 최신과 로컬의 최신이 맞게됨

③ 작업할 브랜치 생성

 - git branch Feat/xxxx

④ 작업할 브랜치로 이동

 - git switch Feat/xxxx

⑤ 개발

⑥ 커밋

 - git add .

 - git commit -m "xxxx 작업 완료"

⑦ github에 push

 - git push --set-upstream origin Feat/xxxx

github에서 PR 날리기

 -  github > Compare & Pull request 버튼 클릭 > compare : Feat/xxxx에서 base : develop 설정 >

내용작성(closes # 입력 시 만들어둔 이슈가 보임, reviewer : 리뷰 해줄사람,  Label : 이슈 분리를 위한 태그) >

Create pull reqeust

 

4.4 피드백 하기 (PL)

① github에서 피드백

 -  github > Files changed > 코드 라인 별로 피드백 가능 > Finished your review 버튼 > Approve 가능

 

4.5 branch 합치기(팀원)

① github에서 merge(PR을 날린 본인이 confirm함)

 -  github > Merge pull request > confirm merge

② PR 성공 확인 및 원격 레포지토리 branch 삭제

 - Pull reqeust successfully merged and closed 확인

 - Delete branch 버튼 클릭(원격 레포지토리에서 내가 생성한 브랜치 삭제)

③ 로컬 브랜치 삭제

 - git switch develop

 - git branch -D Feat/xxxx

※ pull은 항상 develop에서만 함

 

 

※ 실무 실습

https://www.youtube.com/watch?v=qJOfzcMG_hs&list=LL&index=1

 

※ 간략 실습

생활코딩 : 지옥에서 온 Git

반응형
반응형

1. 왜 GIT을 써야하는가

2. GIT의 구조 및 명령어

3. GIT 사용의 예시

4. GIT 실습

 

3. GIT 사용의 예시

GIT을 어떻게 쓸까?

우리는 형상관리를 위해 많은 개발자들이 하나의 저장소를 사용한다. 많은 개발자가 있으니 이를 효과적으로 사용하기 위한 방법에 대한 약속을 정한다. 약속은 대부분 브랜치를 사용하는(따는) 방법이며, 이를 GIT의 브랜치 전략이라고 한다.

 

우리는 브랜치를 어떻게 약속하여 사용하는지에 대해 배울것이고, GIT Flow와 GIT hub Flow 전략을 설명할 것이다.

 

GIT Flow 전략

Master : 라이브 서버에 제품으로 출시되는 브랜치로, 배포 가능한 상태의 버전만을 관리한다.

Develop : 다음 출시 버전을 대비하여 개발하기 위한 주요 브랜치다.

Feature : 기능을 완성 할 때까지 유지되는 브랜치로, 보통 개발자 저장소에만 있고 원격 저장소에는 push하지 않는다.

               Develop 브랜치에서 가져와서 Develop 브랜치로 merge한다.

Release : 다음 버전을 출시하기 위한 브랜치로 Develop브랜치에서 가져와서 Q/A, 테스트 진행 후 문제가 없으면 Master브랜치와 Develop브랜치로 merge한다.

Hotfix : Mater브랜치에서 오류가 발생하면 Mater브랜치에서 가져와서 급하게 수정하여 Master브랜치와 Develop브랜치로 merge 한다.

※ Mater브랜치와 Develop 브랜치는 항상 유지하는 기본으로 두고 나머지 브랜치를 보조브랜치로 사용한다. 

 

 

상황에 따른 브랜치 전략 예시

※ 신규 기능 개발 시 브랜치 전략(Feature 브랜치)

Develop 브랜치로부터 신규 개발할 기능을 위해 Feature 브랜치를 생성한다.

Featrue 브랜치에서 기능을 완성 하면 Develop 브랜치로 merge한다.

 

※ 라이브 서버로 배포하는 브랜치 전략(Release 브랜치)

 Feature 브랜치가 Develop 브랜치로 모두 merge 되었다면, QA와 테스트를 위해 Release 브랜치를 생성한다.

Release 브랜치를 통해 오류가 확인이 되면 Release 브랜치 내에서 수정을 한다.

QA와 테스트가 모두 통과 되면 배포를 위해 Master 브랜치로 merge 한다.

만일 Release 브랜치에서 오류 수정을 했을 경우 동기화를 위해 Develop 브랜치에도 merge 한다.

 

GIT hub Flow 전략 

※ 브랜치 생성 

- 기능 개발, 버그 수정 등 어떤 이유에서든 새로운 브랜치를 생성하되 브랜치 명으로 의도를 명확히 표현해야 한다.

- 새로운 브랜치는 항상 Master 브랜치에서 생성하고 Master 브랜치로 merge 한다.

- Master 브랜치는 항상 최신상태이다.

 

※ 개발 / 커밋 / 푸쉬

- 개발을 진행하며 최대한 상세히 커밋 메세지를 작성하고 수시로 push를 진행한다.

 

※ PR (Pull Request) 생성

- 피드백이나 도움이 필요할때나 merge 준비가 완료 되었을때 PR을 생성하여 pull해달라고 요청한다.

피드백이나 도움이 필요할때 : pull받아서 피드백 좀 해주세요

merge 준비가 완료되었을때 : 최종 merge 하게 pull 받아서 확인 후 push 해주세요.

 

 

반응형
반응형

1. 왜 GIT을 써야하는가

2. GIT의 구조 및 명령어

3. GIT 사용의 예시

4. GIT 실습

 

 

2. GIT의 구조 및 명령어

GIT은 앞서 말한것 처럼 여러 단계에 거쳐 다른 개발자들과 공유한다고 했다.

각 단계별 영역과 상태 등의 구조를 익혀두고 실습을 해보고 다시 이 구조를 본다면 훨씬 이해하기 쉬울것이다.

 

GIT 구조

위의 구조는 영역과 상태, 명령어로 되어있는 모식도이다. 하나하나 설명해보겠다.

 

 

영역
ⓐ Local : 개발자 PC의 영역이다. Local안에서도 여러 가지 영역들이 존재한다. 
ⓑ Working Directory : 실제 개발 하는 파일들이 있는 영역이다.  (Working Tree, Working Copy라고도 부름) 
ⓒ Staging Area : 버전을 만들 파일들을 모아두는 영역이다. working directory에서 여러 파일을 수정했지만, 특정 파일을 현재 버전에 포함하지 않아도 되면 그 파일을 Staging Area에 올리지 않고(제외하고) 버전을 만들수 있다. 즉, 버전을 만들 파일을 선별적으로 묶어 두기 위한 영역이다.(Index, Cache라고도 부름)
ⓓ .git Directory : 버전이 만들어져 관리되는 영역이다. Staging Area에서 묶어둔 파일을 실제로 버저닝이 되어 관리되는 영역이다. (Repository, History, Tree라고도 부름)
ⓔ Remote : 개발자들이 함께 공유하는 서버 영역이며, 원격저장소라 불리운다
ⓕ .git Directory : Remote 서버에서 버전을 관리하는 영역이다.

 

상태

① Untracked : git에서 추적하고 있지 않은 새로운 파일이 생성된 상태이다.

② Tracked : git에서 추적하고 있는 기존에 파일의 상태이며 하위로 2가지 상태를 가진다.

③ Unmodified : git에서 추적하고 있는 기존에 파일을 수정하지 않은 상태이다.

④ Modified : git에서 추적하고 있는 기존에 파일을 수정한 상태이다.

⑤ Staged : 버전을 만들 Stage Area에 파일이 있는 상태이다.

⑥ Version : 버전을 만든 후 .git Directory에 파일이 있는 상태이나, 버전이 만들어진 후 파일은 Tracked > Unmodified 상태로 바뀐다.

 

명령어

git add : 신규 생성된 파일이나, 기존에 git에서 관리하던 파일 중 수정한 파일을 Stage Area에 올릴 경우 사용하는 명령어이다. (Untracked 상태 → Staged 상태 , Modified 상태 → Staged 상태로 바꿈)

git commit : 버전을 관리할 Stage Area에 모인 파일들을 버저닝하는 명령어 이다. (Staged 상태 → Unmodified 상태로 바꿈)

git push : 버저닝한 파일들을 원격 저장소에 업로드하는 명령어이다.

git pull : 버저닝한 파일들을 원격저장소에서 다운로드 하는 명령어이다. 이렇게 다운로드 된 파일은 Tracked > Unmodified 상태로 다운로드 된다.

 

 

 

반응형
반응형

1. 왜 GIT을 써야하는가

2. GIT의 구조 및 명령어

3. GIT 사용의 예시

4. GIT 실습

 

1. 왜 GIT을 써야하는가

세상에 거의 모든 현업 개발자들은 혼자 개발하지 않으며, 소스들은 개발자들의 손을 통해 지속적으로 바뀐다.

이러한 특성으로 인해 소스코드들을 체계적으로 관리해주는 것 필요했고 이러한 일을 형상관리라고 한다.

우리도 일상적으로 형상관리를 해본일이 있다.

 

발표자료를 팀원들에게 공유 하는 상황을 그려본다.

 

최종.pptx

진짜 최종.pptx (아이콘 수정)

진짜 진짜 최종.pptx (진짜 최종.pptx + 피드백 자료 합치기)

진짜 이게 찐 최종.pptx (오타 발견)

 

이런 파일 공유 많이 해봤을 거라 생각한다. 상황은 다음과 같다.

최종.pptx를 받은 팀원이 최종_피드백.pptx로 피드백을 했다.

아이콘 이미지를 바꾸기 위해 난 이미 진짜 최종.pptx를 저장해서 공유하려고 했다.

피드백을 받았으니 둘을 합쳐 진짜 이게 최종.pptx가 만들어진다.

근데 또 오타를 찾았다......

진짜 이게 찐 최종.pptx가 만들어진다.

 

위와 같은 상황을 잘 관리할 수 있게 도와주는 툴(프로그램)을 우린 형상관리 툴(프로그램)이라고 한다.

 

형상관리 툴은 다른 개발자와 같은 파일을 수정하거나 개발했던 것을 원복해야할 때 등 좋지 못한 상황이 발생했을 때도 큰 힘을 발휘한다.

 

이 형상관리 툴은 SVN과 GIT이 대표적이다. 다만, GIT은 혁신이라고 불리우는 대신 지옥에서 왔다는 별명을 가지고 있다.

 

둘의 특징은 다음과 같다.

SVN은 직관적이기 때문에 사용하기가 편하다.

SVN서버에 저장소를 만들고 거기에 모든 개발자들이 연결하여

중앙 집권식으로 소스를 업로드(commit), 다운로드(update)하게 된다.

이 말은 SVN서버와의 연결이 끊어지면, 업로드 다운로드가 안 될뿐아니라

소스를 수정했던 이력들을 볼수 없고, 서버가 수명을 다 한다면 수정 이력들을 날려버리게 된다.

내 로컬 PC에 소스는 남아있으니 SVN서버를 다시 파서 개발자들에게 이쪽으로 붙으라고 하면 되지만,

여기서 GIT이 좋은 이유를 찾을 수 있다.

 

GIT은 여러 단계를 거쳐 다른 개발자들과 소스를 공유하게 되는데,

단계가 많다보니 단계별로 발생 할 수 있는 경우의 수가 많다.

그럼에도 불구하고 GIT이 가진 장점으로 위와 같은 상황에서 힘을 발휘한다. 

 

GIT은 개발자들마다 독립적으로 저장소를 가지고 있고 관리가 가능하기 때문에

GIT 서버가 어떻게 되든 히스토리를 똑같이 복구 할수 있다.

심지어 GIT의 서버는 github.com 사이트에서 서버로 활용할 수 있어, 서버 구매 비용도 없고 서버가 잘못될 가능성도 현저히 적다.

 

히스토리가 왜 중요할까?

더보기

소스를 복원해야하는 경우가 생길 수 있다.

결정권자의 횡포로 아 이거 전에거가 더 나은대? 원래대로 돌리자 라거나,

심각하고 치명적인 오류를 뒤늦게 발견했지만, 고치려면 시간이 오래 걸린다면 이전 상태로 돌려야한다.

 

말그대로 소스의 역사를 통한 정보 습득이다.

누가 언제 어디서 무엇을 어떻게 소스를 작성했는지 알고 여러가지 일을 할 수 있게 된다.


 

다음으로 GIT은 개발자 자신만의 버전관리를 할 수 있다.

버전 관리라고 함은 어떠한 기능을 만들때 썼던 여러가지 소스들을 유의미한 단위로

묶어서 관리한다는 이야기이다.

만약, 로그인 기능을 만든다고 하면 로그인 기능을 업데이트한 버전을 묶어서 저장소에 저장을 한다.

이렇게 관리된 버전들은 명령어 하나로 아주 쉽게 이전 상태의 버전들로 돌아갈 수 있다.

 

세번째 GIT은 Branch와 merge 기능을 사용하기에 좋다.

갑자기 처음 보는 용어가 나와서 당황 했을 것이다. 한국말로 브랜치와 머지라고 하는데

위의 최종.pptx사태로 돌아가 보자. 

(개념적으로만 이해하고 넘어가자. 실습으로 이해하는게 더 빠르다.)

 

상황

최종.pptx를 팀원에게 공유하고 혹시 피드백이 올지 몰라

최종.pptx를 기반으로 따로 진짜 최종.pptx 파일로 작업한다.

아니나 다를까 최종_피드백.pptx가 피드백이 왔고

진짜 최종.pptx최종_피드백.pptx를 합쳐 진짜 진짜 최종.pptx를 만들었다.

 

해석

2번째 줄인 최종.pptx를 기반으로 따로 진짜 최종.pptx 파일로 작업한다.

는 말을 최종.pptx에 브랜치따서 작업 한다고 얘기한다.

 

4번째 줄에 최종_피드백.pptx진짜 최종.pptx를  합쳐 진짜 진짜 최종.pptx를 만들었다.는 얘기는

브랜치로 딴 진짜 최종.pptx와 원본 파일의 수정본인 최종_피드백.pptx를 머지 한다고 한다.

 

 

최종 정리

SVN

- 직관적이고 사용하기 편하다.

- 중앙 집권식으로 소스들을 다운로드 업로드 한다.

- 서버가 죽으면 히스토리를 살릴 수 없다.

 

 

GIT

- 독립적인 저장소를 관리한다.

- 버전 관리를 개발자가 직접 할 수 있으며, 버전별 소스 상태 변경이 용이하다 

- branch와 merge 기능을 사용하기 좋다.

반응형
반응형
이 글은 문과생에서 공학도로, 비전공자에서 개발자로서 8년만에 처음 작성하는 회고이다.
반면교사 삼아 나와 같은 상황을 겪지 않았으면 하는 바람으로 작성해본다.

 

고등학교 시절 수학이 싫어 문과를 선택했고,

문과생에서 좋아하는 과목을 따라 공학도의 길을 선택했다.

 

졸업 후 취업을 알아보던 중 우연한 계기로 웹 개발자 양성 학원을 다니게 되었고,

이 후 개발자의 삶이 시작 되었다.

 

그 당시 치열하게 고민했겠지만, 이 선택들이 가지는 무게감은 상당했었다.

문과생이 공학도의 길을 선택 했을 땐 공학계열 과목은 출석으로 C 학점을 채우기 급급했고,

졸업 후 개발자 양성학원에서는 타자 따라치기 급급했었다.

 

꾸준함과 노력이 무기라고 생각하여 이해가 안가고 무슨 말인지도 몰랐지만 무작정 앉아있었다.

다행히 졸업학점은 3점대를 넘겼고, 3개월 학원 수료 후 1개월 만에 첫 직장에 입사하였다

 

 

SI 2년

첫 직장은 전공과 관련된 공공기관 SI 웹 개발을 하는 회사였다.

학원에서 타자만 치기 바빴던 나는 Java도, 쿼리도 모르는 갓태어난 송아지였다.

제안서를 쓰고 문서작성하는게 오히려 회사에 쓸모있는 사람이라는 생각이 들었다.

 

하지만 어김없이 개발일은 들어왔고,

선임 개발자가 짰으면 더 빨랐을 일을 선임 개발자의 도움을 받으며

소스를 어디에 붙여넣어야 오류가 안날지 고민하는 개발을 했다.

 

처음 javascript로 별거 아닌 함수를 만들고 호출하며 세상 뿌듯해했던 기억이 있다.

정말 단순한 함수였는데 혼자 뭔가 만들었다는 사실이 좋았던것같다.

 

이렇게 제안서 2개와 프로젝트를 2개를 하면서 CRUD 경험을 하게 되었다.

이때 역시 나의 개발 실력은 코드를 어디에 붙여넣어야 오류가 안나는지 아는 정도.....

 

부족함은 알고있지만

어떤 공부를 해야할지, 어떻게 공부해야할지, 뭐가 얼마나 부족한지 전혀 몰랐다...

아니, 공부의 절실함도 사실 잘 몰랐던것 같다.

 

 

SM 6년

개발도 못하면서 월급은 더 받고 싶어 취업 제안을 뿌리치지 못해 SM으로 입사를 했다.

이때 연봉이 1.5배 이상 뛰었고,

소스 붙여넣기만 했던 3년차 개발자도 이런 대우를 받을 수 있구나

라는 오만함이 들었던것같다.

 

입사를 해보니 기획자 1명, 개발자는 3명, 퍼블리셔 1명이 있었다.

SM이지만 전문 IT 회사가 아니였고, 개발에 대해 아는 사람은 개발자 3명이 전부였다.

 

처음 1~2년은 정말 배울것이 많았다.

제로 베이스였으니 소스를 더 잘 붙여넣을 수 있게 되었으며,

게시판을 혼자 만들수 있게 되었고,  쿼리를 짤 수 있게 되었으며,

오류가 발생하면 뭐부터 해야하는지 알게 되었다.

 

하지만, 같은 일 반복이 되어 익숙해졌고, 야근은 없었으며, 월급은 하는일에 비해 많았다.

심각한 메너리즘에 빠져있다는 것, 발전을 하고 있지 않다는 것이 느껴져

입사 3년 후 블록체인 관련 개발 학원을 결석 한번 없이 수료하였다.

수료 후 입사를 제안한 곳도 있었지만, 자신도 없었고 이곳을 포기할 순 없었다...

 

학원 수료 이후에도 사이드로 프리 일을 하기도 하고, 개인 프로젝트를 하면서

나름 열심히 하고있다는 스스로 위로를 했다.

아니, 사실 좀 자신감도 생겼었다.

예전에는 꿈꾸지도 못했던 일을 해냈다.

혼자 개발하고 인터넷 검색해가며 서버도 띄우고 배포도 했다.

난 풀스텍 개발자라 생각했던 것이다...

 

이렇게 SM에서 6년이란 시간이 지났다.

회사 사정으로 이 사업이 철수가 된다.

 

현재 취업 시장에서 나는 9년차 개발자로서 경험이 부족한 물경력 시니어였다.

전 회사의 괴물 선임은 그 동안 뭐했냐며 진심으로 안타까워 했고,

면접에서는 3~4년차 주니어 정도 된다는 쓴소리도 들었다.

 

한 동안은 이런 생각을 했다.

내가 이 길을 계속 가는게 맞는건가.

안가면 내가 할 수있는게 무엇인가.

BackEnd로 갈것인가 FrontEnd로 갈것인가.

 

하지만 결론은 가야한다.

다른길이 없다.

현재 난 많이 부족하고, 공부 말곤 극복 할 수 있는 방법이 없다.

이런 자극, 절실함을 느낀지 1개월 정도 되었고 곧 실업자가 된다.

 

 

같은 실수는 반복하지 않는다.

이런 글을 작성한다는 것이 얼마나 창피한 일인지 모른다.

익명이라는 가면을 쓰고있긴 하지만 나의 치부를 이렇게 드러내본적이 없다.

그럼에도 이 글을 쓰는 이유는 더 발전하기 위해서이다.

서두에 반면교사 삼아 같은일을 겪지 않았으면 한다고 포장했지만,

더 큰 이유는 과거를 돌아보고 발전하기 위해서가 더 크다.

 

현재 난 절실함을 느끼면서 이렇게 하고있고 하려고 하고있다.

1. 면접용 질문 정리

면접용 공부는 따로 있다. 면접용 공부라고 하긴 하지만 면접에서만 쓰는건 아니라는 생각이 든다.

알고 있던 것을 정리하는게 아니라 정리하면서 알게 되니 더 그렇게 느끼는것 같다.

웹에서 긁어다가 내 블로그에 정리하는 것 말고, 수기로 쓰면서 공부하고 있다.

Java, Spring, web/network, 기타 섹터로 나눠서 연습장에 한번 정리 노트에 한번 쓰면서 공부하고있다.

 

 

2. 개발 관련 짤막한 유튜브 시청

여지껏 유튜브는 놀이의 수단이였다. 이제 유튜브의 히스토리는 개발 관련된 것으로만 채워지고있다.

생활코딩, 얄팍한코딩사전, 널널한개발자, 우아한테크, 박재성, 코딩애플을 구독하고 관심있는것 위주로 시청하고있다.

이 구독 목록은 다른 분들에 비해 쉽게 설명해주는 장점이 있어 개인적으로 개념 공부하기 너무좋다

더 딥하게 공부하려면 이것으로 부족하긴 하겠지만, 현재는 이게 최선이다.

 

 

3. 토이프로젝트

경험이 부족하니 채우는 수 밖에 없다. 현재 BackEnd 시장에서 가장 많이 쓰고 있는 기술인

spring boot, jpa, git을 경험해보지 못했다. 멤버를 찾지 못해 계속 서칭중이긴 하지만 혹시 못찾는다면,

혼자서라도 해볼 생각이다.

 

 

4. 기초 공부

개발 기본 : 운영체제, 컴퓨터 구조, 네트워크

BackEnd 기본 : Java, Spring, GIT, 디자인 패턴

9년차라면 : 도커, 쿠버네티스, cloud 서비스, 젠킨스 설정, 리눅스, 웹크롤링 등

 

부족한게 너무 많지만 BackEnd 기본 부터 공부 하려고 한다.

Spring은 김영한선생님의 인프런강의, GIT은 생활코딩, 자바는 남궁성님 유튜브강의, 디자인패턴은 헤드퍼스트를

참고할 예정이다.

 

 

5. 커뮤니티 활동

나의 현재 실수의 가장 큰 원인은 오만함과 메너리즘이였다.

이런 것을 주변사람들과 이야기를 나눴다면, 절대 있을 수 없는 일이라 생각이든다.

내가 사는곳은 경기도라 커뮤니티가 많이 없지만, 멀어도 최대한 참여해보려고 한다.

 

 

6. 개발 관련된 독서

개발 필독서가 있다고 한다. Java도 잘 모르는 내가 오브잭트라던가 클린코드라던가 리팩토링같은

공부를 할 엄두가 안난다. 그래서 자꾸 기초에 목매달고있는것 같지만 언젠간 깨부숴야할 것들이라

생각이 들어 하나한 공부할 예정이다.

현재 맨탈이 많이 깨져있는 상태라 프로그래머의 길 멘토에게 묻다라는 책을 구매했다.

 

 

내가 하고 있는 일이 사회적으로 얼마나 가치있는지를 판단해야한다.

사회적 공헌의 얘기가 아니라 얼마의 돈이 오가는지에 대한 얘기이다.

나에게 이런 돈을 쥐어 줄만한 일을 하고 있는지 끊임 없이 의심해야한다고 생각한다.

 

누군가 얘기했던것같다.

"언젠간 짤리고 회사는 망한다."

회사가 망하면 다른 곳에서 일 할 수있는 역량을 키우기 쉬운 직종이 개발자라 생각한다.

이 길을 선택했으니 이 길의 단점보단 장점을 보고 달려나갈 생각이다.

 

끊임없이 공부해야하는 직업.....

이제서야 몸소 깨닫는 중이다.

 

좋은 개발 문화를 가진 좋은 회사에 입사 후 좋은 내용으로

회고를 쓸날을 기약하며

긴글을 마친다.

 

 

- 맨탈적으로 힘들어하고 있을 모든 비전공 개발자들을 위해...

반응형
반응형

select * from user_source
where type in ('PROCEDURE','FUNCTION','PACKAGE BODY')
and text like '%domain%'
order by name, line;

반응형
반응형

에러메세지 : was에서 안뜸

 

상황 : 파일 업로드 시 로컬에서는 정상작동 하나 서버에 올리니 controller에도 접근이 안되고 개발자도구에서 500 error 확인

 

원인 : was 서버 세팅에 로컬과 다르게 multipart context 설정이 안되어 있었음.

 

해결 : servers > was 서버 명 > context.xml에 추가 

<Context allowCasualMultipartParsing="true" path="/">

<Resources cachingAllowed="true" cacheMaxSize="100000" />

 

※ web.xml에 500 error 페이지 설정을 해놔서 

500 error 발생 시 에러 메세지에서 힌트를 얻을 수 없었음

500 error 페이지 설정을 걷어낸 후

개발자도구의 네트워크 > 500 error 발생한 url 클릭 > Response에서 하기의 오류 메세지를 보고 처리 할 수 있었음


"org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: 어떤 multi-part 설정도 제공되지 않았기 때문에, part들을 처리할 수 없습니다."

반응형

+ Recent posts