Maven을 이용해서 신규 프로젝트 만들기

한땀한땀 손으로 Maven 프로젝트를 만드는 것도 의미있는 일이지만 귀찮다.

mvn archetype:generate -DinteractiveMode=false -DarchetypeArtifactId=maven-archetype-quickstart \
-DgroupId={project-packaging} -DartifactId={project-name}

와 같은 형태로 잡아주면 된다.
최근 개발은 Spring Boot를 많이 이용하기 때문에 여기에서 주로 쓸만한 archetype들을 나열해보면

  • maven-archetype-quickstart
  • spring-boot-sample-simple-archetype
  • spring-boot-sample-data-jpa-archetype
  • spring-boot-sample-actuator-log4j-archetype

Spring에서 사용할 수 있는 전체 Archetype 목록은 여기에서 확인 가능하다.  다만 Spring 기반으로 프로젝트를 생성시킬려면 기본 archetypeArtifactId 이외에 archetypeGroupId=org.springframework.boot 값을 추가로 줘야한다.

mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=org.springframework.boot -DarchetypeArtifactId={spring-archetype} \
-DgroupId={project-packaging} -DartifactId={project-name}

가장 대표적인 API 개발용 명령을 이용하는게 가장 깔끔하다.

mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=org.springframework.boot -DarchetypeArtifactId=spring-boot-sample-simple-archetype \
-DgroupId={project-packaging} -DartifactId={project-name}

그 다음에 생성된 pom.xml 파일의 spring-boot-starter-parent 의 버전을 1.3.3.RELEASE로 변경한다. CORS 지원과 몇가지 기능을 사용할려면 이 이상 버전을 사용하는게 좋다. 추가적으로 다음의 Dependency들을 pom.xml에 반영하면 즐거운 코딩 생활에 도움을 얻을 수 있다.

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.8</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.4</version>
    </dependency>
    <!-- for db programming with mysql -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
      <scope>runtime</scope>
    </dependency>

한가지를 더 추가하자면 람다등을 사용할려면 자바 컴파일 환경을 1.8 이상으로 설정하는게 편하다. 다음 빌드 플러그인을 설정해두면 대부분의 IDE에서 인식한다.

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>

즐 코딩~

 

JUnit Parameterized Test – 반복 테스트를 하는 뻔한 방법

JUnit에서 조건값을 바꿔가면서 테스트를 해야하는 경우에 이 방법을 사용하는게 테스트 비용을 아끼는데 좋다.

@RunWith(Parameterized.class)
public class PrimeNumberCheckerTest {
    private int inputNumber;
    private boolean expectedResult;
    private PrimeNumberChecker checker;

    @Before
    public void setup() {
        checker = new PrimeNumberChecker();
    }

    @Parameterized.Parameters
    public static Collection<PrimeNumberValidationInput> primeNumbers() {
        return Arrays.asList(new PrimeNumberValidationInput[] {
                new PrimeNumberValidationInput(2, true),
                new PrimeNumberValidationInput(6, false)
        });
    }

    public PrimeNumberCheckerTest(PrimeNumberValidationInput input) {
        this.inputNumber = input.number;
        this.expectedResult = input.expectation;
    }

    @Test
    public void testCheckerValidatePrimeNumber() {
        assertThat(checker.validate(inputNumber), is(expectedResult));
    }
}

예제에서는 테스트를 위한 입력으로 PrimeNumberValidationInput이라는 클래스를 사용했다. 다른 책에서 예제를 인용할 때는 보통 Object[][]을 이용하는 경우가 많던데, 물론 간단한 수치 입력을 하는 경우에는 이 방법을 사용해도 크게 나쁘지는 않은 것 같다. 하지만 Object 변수를 사용한다는게 뭔가 테스트 코드의 품질을 떨어트리는 것만 같은 후질근함을 내보하기 때문에 별로 좋아보이지는 않는다. 더구나 코드의 의미 부여적인 측면에서 각 변수의 이름을 살려주는게 테스트의 맥락에서 좀 더 좋아보이기 때문에.

    public static class PrimeNumberValidationInput {
        public int number;
        public boolean expectation;

        public PrimeNumberValidationInput(int number, boolean expectation) {
            this.number = number;
            this.expectation = expectation;
        }
    }

이런 클래스 하나쯤 테스트 코드에 심어주면 간지나지 않을까 싶다. 테스트를 위해 투자하는거 넘 인색하지 말자. 테스트도 관리해야할 코드 가운데 하나니까 말이다.

Thunderdome – 개발자의 재미, 개발하기

2주일간의 출장을 마치고 복귀했다.

몇번 가지도 않았지만 뱅기타고 가는 출장에서는 풀어내야할 꺼리들이 한가득이었다.  그리고 시간도 부족했다.  일에 대한 압박감도 있고, 제대로 되지도 않는 영어로 이야기를 하자니… 출장가서 잠을 제대로 못자는건 단순히 시차 적응 실패가 가장 큰 원인이겠지만 이런 류의 스트레스도 한몫을 한다고 본다.

이번 출장은 좀 다른 목적을 가지고 있었다.  썬더돔(Thunderdome)이라는 회사 내부에서 하는 핵커톤 행사에 참가하고, Architecting Conference에 참가해서 배움을 넓히는 시간이었다. 한마디로 개발자스럽게 놀다가 오는 시간이랄까? 하지만 피곤한건 변하지 않는다.

thunderdome8-after

처음 시작하는 모습은 그럭저럭 괜찮긴 했지만, 해커톤 행사 하루만에 확연하게 거북목 증세를 보인다.  거북목을 피할려고 받침대랑 키보드랑 마우스도 바리바리 챙겨갔음에도 불구하고 이런 자세가 되는건 아무래도 병인 것 같다.

thunderdome8-before

이런 상태로 3일을 정말 열심히 코딩을 했던 것 같다.  물론 팀의 성과는 좋았다. 다만 내가 기여할려고 했던 부분은 제대로 돌리는데 실패했다. 너무 자신만만하게 문제를 대했던 것이다.   덕분에 내부 구조가 어떻게 돌아가고 뭐가 문제인지는 확실하게 알게 됐지만… 이걸 제대로 만드는건 3일동의 작업만으로는 택도 없다라는 진실을 깨우쳤다.

하지만 같이 간 다른 친구들이 정말 엄청나게 일을 잘 해줬다. 덕분에 원래 의도에 거의 부합하는 작품을 만들어냈다.  시연 시간에는 덕분에 사람들의 관심이 폭발해서 참가한 보람을 많이 찾을 수 있었다. 3일 동안 어떻게든 재미있게 개발을 했던 시간이 나름 유종의 미를 거두니 잠못잔 시간이 아깝게 느껴지지는 않았다.

thunderdome8_converted

나중에 제대로 만들어진다면 그때 제대로 기여할 수 있지 않을까 하는 생각이다.

LA에서 일정은 정말 피곤한 3일을 보내는 것으로 마무리했다. 한인 타운에서 한잔 술 털어넜고 같이 했던 친구들과 재미있는 이야기도 나누니 정말 좋았다.  이런 좋은 시간을 뒤로하고 뉴욕으로 컨퍼런스 들으러 이동했다.

혼자 LA에서 NYC로 이동하는 것도 또 다른 하나의 도전이다. 혼자 출장온 경험도 있긴 했지만 그때는 대한항공을 타고 오니 낯설음이라는게 적었다. 하지만 AA 항공타고 미국 국내선으로 이동하는 길에 한국인은 딱 나 혼자였다.  혼자서 이런 저런 일들을 다 처리해야한다고 생각하니 정말 깝깝스러웠다.

가장 큰 문제는 이야기할 사람이 없다는거.  회사에서는 그래도 업무적으로 이야기할 사람이라도 있었고, 지역 오피스를 배려하는 좋은 분위기가 있어서 이야기는 것도 훨씬 수월했다. 컨퍼런스에서 막다뜨린 사람들과 뭐라도 한마디 말을 섞어볼려고 해도 언어적인 장벽이 전혀 만만하지 않았다. 기술 언어를 하면 될거라고 생각했지만 사람들은 생활 영어를 한다는거.  이런게 또 다른 Pain Point!!

대강 알아들으면서 대강 이야기를 하면서 여기서도 4일간 고생을 했다.  2일은 Traning 세션이었고 2일은 Conference였는데 나름 의미는 있었다. 트레이닝 세션으로 들었던 Docker 관련 사항은 회사의 시스템 방향이기도 했기 때문에 짧게라도 Docker를 경험해볼 수 있는 좋은 시간이었다.   물론 따로 공부할려고 책도 읽는 중이었지만 단순히 눈으로 읽는 것과 실제로 손으로 타이핑을 쳐보는 건 완전히 다른 경험이니 말이다.

사진에 있는 스트라이브를 입으신 분이 주로 Docker를 이용한 Architecting에 대해 강의를 진행했는데 영국 억양이 상당히 인상적이었다.  삐쩍 말랐을 거라고 생각했는데 와중에 올챙이 배를 봤을 때는 웃음이 절로 나왔다.  역시 나이를 먹으면 나오는게 똥배구나…

다행이 맨하튼에 한인 타운이 있어서 밥은 정말 잘 먹었다. LA에서 먹는 버거나 피자 혹은 현지 음식들은 상당히 소금을 많이 쳐서 별로 땡기지 않았는데, 뉴욕은 음식이 짜지 않았다. 🙂 이것만으로도 정말 다행이었다.  물론 저녁은 항상 한식이었지만.

wp-1461337451094.jpeg

대도시의 백미는 역시 네온사인이었던 것 같다. 영화나 미드에서 종종 나오던 골목을 실제로 걸을 수 있었던 것만으로도 좋았던 경험이지 않았을까 싶다.

git의 ssh key 설정하기

추가하는 방법은 git의 사용자 셋팅에 SSH Key추가하기를 하면 되는데…

https://help.github.com/articles/generating-an-ssh-key/

이미 예전에 추가를 해놨음에도 불구하고 왜 맥 터미널에서 자꾸 passphrase를 입력하라구 물어보는거야!!

와중에 이전에 입력해둔 문구를 까먹었는데!!!

별다른 방법이 없다. 다시 만들고 등록해두는 방법 밖에는.

다 하고 다음 명령어로 넣어두면 이후에는 안물어볼 것 같다.

ssh-add -K

SonarQube 이용해서 만드는 CI

CI 들어봤나?

Continuous Integration의 약자이다. 해석하자면 “지속적으로 통합하라” 라는 이야기다.  뭘? 코드를.  누가 작성한 코드를?  여러분과 여러분들의 동료들이 작성한 코드를 말한다.

왜 통합을 해야할까?

이유는 간단하다. 통합을 위해서.

뭔 소리냐구?

통합을 지속적으로 시도하는 이유는 바로 필요한 시점에 사고 터지는 걸 막기 위해서다.  누구나 익히 알고 있듯이 모든 개발은 팀 작업이다.  혼자 잘해서 되는 개발은 더 이상은 없다.

사람들이 하나의 목표로 달린다지만 속된말로 꿍꿍이는 다 다르다.  이런 불일치가 지속된다고 가정해보자.  계속 내 일은 내가 알아서 하는 상태로, 너와 나의 일은 깔끔한 문서로 정리되어 있다면?

여러분들은 이들의 작업이 성공적인 결합으로 결론지어질 수 있을거라고 생각하나? 절대 불가능하다.

어떻게해야 할까?

가장 현실적이고 최선인 대답은 가능한 빠른 시간에 합쳐보라는 것이다.  “가능한 빨른 시간”라는 말은 여기서 두 가지 의미를 내포한다.  첫 번째는 현재를 기점으로 가장 이른 시간에 합쳐보는 시도를 해야한다.  두 번째 의미는 이전에 해 본 이후그 다음 번 합칠 때까지 시간을 늦추지 말라는 것이다.  나뉘어져 개발되는 결과를 합쳐보는 시도는 당연히 빨리 해야한다. 아마도 모두가 동의하는 사실일 것이다.

두번째 그럼 얼마나 빨리?  CI라는 단어를 처음 이야기한 Grady Booch님은 종종(?)을 이야기했다. 하지만 최근 CI를 열창(!)하는 에자일이나 XP쪽에서는 가능한 많이 하루를 넘기면 죄악(?)이라고 이야기한다.  개인적으로 동감하는 바이다. 하지만 죄악은 일상의 연속이며 실행하기는 참 어렵다. 그래서 회개는 반드시 해야하는 모양이다.

Best Practices

그럼 CI를 실행하는 최선의 방법은 뭘까?  위키에서 언급된 부분을 번역 수준에서 읊어보자면…

  • 코드 저장소(Code Repostiory)
  • 자동화된 빌드
  • 자동화된 테스팅
  • 일관된 커밋 규칙을 갖기 – 이건 전체 참여자들이 다들 지키는 규칙을 만드는게 어려운 것 같다.
  • 동작 가능한 커밋하기
  • 빨랑 빌드되기
  • Stage 환경 갖추기
  • Nightly build 환경 갖추기
  • 개방된 CI 결과 – 쪽팔린다고 테스트 결과를 숨기거나 빌드 오류를 숨기지 않는다.
  • 자동화된 배포

이걸 종합해보면 아래와 같은 순환적 코드 관리가 이뤄지면 된다.

개발자들은 개발된 결과를 코드 저장소에 저장하고, 저장된 결과는 자동화된 빌드 및 테스트를 통해 결과가 올바른지 확인하고 그 피드백을 통해 신규 코드를 추가하거나 기존 코드의 개선을 진행한다.

적어도 통합은 여러분이 일하는 그 날의 마지막 작업이어야 한다.  가능하면 그 결론이 성공적인 통합인 것으로!

And Then…

그런데 CI의 개념은 알겠지만 이걸 어떻게 만들어야 하는거지?  지금까지 이야기를 정리하면 CI를 구현적 관점에서 만들려면 다음 두 가지 기술이 필요하다.

  • 주기적으로 뭔가를 돌려주는 배치 시스템 – 이거에 딱인 시스템을 우리는 잘 알고 있다. 바로 젠킨스(Jenkins)!
  • 소스 코드의 “분석/빌드/테스트” 결과를 수집해서 리포팅 해주는 물건 – 이 물건이 바로 소나큐브다.

이제 본격적으로 알아보도록 하자.

 

SonarQube

네이버에서는 클로버(Clover)라는 툴을 사용했다. Atlanasian에서 만든 툴인데 기억에 상당히 비쌌던 것 같다. 찾아봤더니. 1년에 이정도면 회사에서 사용하기에 그리 비싼 도구는 아닌 것 같긴한데? 하지만 역시나 빈한한 개발자에게는 무리가 되는 금액이긴 하다.

clover-price

소나큐브는 오픈소스다. 다른 말로 하면 공짜다.  친절한 유지보수는 없지만 덕을 볼려는 개발자들에게 이런 정도의 수고는 의무가 아닐까?

What is it?

근데 정확하게 소나큐브(SonarQube)가 뭐하는 거죠?

앞에서 이야기한 것처럼 소나큐브의 기본 역할은 정적 분석이고 여러분이 작성한 테스트와 꿍짝해서 코드가 테스트에 의해 얼마나 검증됐는지 커버리지(Coverage)를 측정한다.

  • Complexity
  • Duplications
  • Coding Rules
  • Potential Bugs
  • Test Cases
  • Coverage

 

Installation

젠킨스(Jenkins)를 써야겠지? 맥이나 아마 리눅스에선 다음 명령을 이용하면 젠킨스는 바로 설치할 수 있을 것이다.

sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
sudo rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
sudo yum install jenkins

만약 젠킨스가 깔리 서버에 JDK8이상이 설치되지 않았다면 이것도 설치해야 한다.  신상을 원한다면 그건 알아서 확인하자.

curl -v -j -k -L -H "Cookie: oraclelicense=accept-securebackup-cookie" \
http://download.oracle.com/otn-pub/java/jdk/8u73-b02/jdk-8u73-linux-x64.rpm > jdk-8u73-linux-x64.rpm

sudo rpm -ivh jdk-8u73-linux-x64.rpm

인제 준비 완료. 정말 소나큐브를 깔아보자.  먼저 해야할 일은 최신 버전을 다운로드 받는거다.  http://www.sonarqube.org/downloads/ 에서 얻을 수 있다.  최신 버전을 확인한 다음 다음의 과정을 죽 진행하면 설치가 마무리된다.

wget https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-5.4.zip
unzip sonarqube-*.zip

# setup for auto execution when reboot
sudo ln -s $SONAR_HOME/bin/linux-x86-64/sonar.sh /usr/bin/sonar
sudo chmod 755 /etc/init.d/sonar
sudo chkconfig --add sonar

# start sonar
sonar start

Connecting to Jenkins

소나큐브 서버까지 설치를 했으면, 다시 젠킨스와의 연결 고리를 만든다. 젠킨스에서 지원하는 훌륭한 플러그인들이 많은데 우리가 할 일은 그것들을 콘솔상에서 깔아주기만 하면 된다.  대부분은 이미 설치되어 있고 윤택한 개발을 위해 다음 플러그인들만 추가로 설치한다. “젠킨스 관리 > 플러그인 관리” 메뉴를 통해 추가로 플러그인을 설치할 수 있다.

  • Git client plugin
  • Git plugin
  • Maven integration plugin
  • SonarQube plugin

자, 여기까지 했으면 다음으로 이것들에 대한 설정을 잡는다. 설정은 젠킨스 전역 설정이 있고, 개별 프로젝트 단위로 해야할 설정이 따로 있다.

전역 설정

설정은 “젠킨스 관리 > 시스템 설정” 메뉴에서 잡는다.  다른 플러그인은 알아서 깔면 될 것 같고,  소나큐브에 대한 설정은 아래 화면을 참고한다.

sonarqube-jenkins-config

잡아줘야 할 항목들 가운데 대부분은 기본 값으로 설정한다.  만약 본인이 이전 소나큐브 설치 과정에서 변경을 했다면 해당 값을 여기에 입력한다.  예를 들어 기본으로 제공해주는 embeded DB가 아닌 MySQL DB를 사용하는 경우에는 해당 값을 반영해야겠다.

주의해야할 필드는 “Additional analysis properties” 항목 값이다. 다음 2가지 속성에 대한 값이 반영되야한다.  각 속성에 여러 값을 입력하는 경우는 콤마(,)로 구분하면 되고, 이 두개 항목의 구분은 세미콜론(;)으로 구분한다.

  • sonar.sources
  • sonar.java.binaries

만약 다른 프로그래밍 언어 혹은 개발 플랫폼을 이용한다면 그 값을 프로젝트별 설정에서 추가로 설정해줘야 한다.

프로젝트별 설정

우리는 자바 개발을 좋아하는 개발자이기 때문에 자바, 메이븐(Maven) 기준이다.

메이븐에서 커버리지를 측정할려면 각 코드가 어떻게 수행되는지를 측정해야 한다.  소나큐브와 가장 많이 어울리는 툴이 Jacoco라는 툴이다.  물론 이 측정이 일반 메이븐 필드 과정에 껴들면 곤란하기 때문에 일반 빌드와는 별도로 설정하도록 되어 있으며 이를 위한 설정은 아래 plugin 설정으로 묶어서 build  과정에 포함시켜둔다.

      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.7.6.201602180812</version>
        <executions>
          <execution>
            <id>default-prepare-agent</id>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

pom.xml 파일에 이렇게 추가하고, 이제 젠킨스 프로젝트 설정을 보자.  플러그인이 돌아야지  커버리지를 볼 수 있기 때문에 빌드 Goal을 아래와 같이 설정한다.

sonarqube-jenkins-proj-config

  • Goals and options: clean org.jacoco:jacoco-maven-plugin:prepare-agent install
  • Analysis properties
    • sonar.projectKey
    • sonar.projectName
    • sonar.projectVersion

여기에서 설정하는 project 필드는 소나큐브의 프로젝트를 정의한다.  여기에서 이름이나 버전등을 변경하면 새로운 프로젝트 소나큐브에 만들어진다. 만약 전역 관점에서 sources, binaries 들을 설정하지 않았다면 여기에서 추가로 설정할 수 있다.

설정을 마치고 메이븐 프로젝트를 빌드해보자.  그럼 보게 될 것이다!!

sonarqube-installed

 

Ember.js – Summary of core concept and usage

현재 개발하고 있는 Single Page WebApp의 기반 프레임워크로 Ember.js를 사용중이다. 한번 작업을 할 때는 대강 구조를 알겠다 싶었지만 몇년을 손에 익힌 언어와 프레임웍이 아니다보니 자꾸 헷갈리게 된다.  다시 한번 머리속의 내용을 정리하는 차원에서 기록해둔다.

대부분의 핵심 내용은 Ember.js 사이트에서 자료를 구했다.  여기에서는 업무를 진행하기 위해 알고 있어야 하는 부분들만 추려봤다. 좀 더 상세한 자료는 아래 사이트에서 찾아보면 상세한 자료를 얻을 수 있다.

Ember.js: https://guides.emberjs.com/v2.4.0/getting-started/core-concepts/

Ember.js Workflow

Ember의 간단한 흐름은 아래와 같다.  시나리오는 사용자가 http://localhost:4200/rentals 라는 URL로 접근하는 경우이다.

  • 라우터로 “rentals”를 등록한다.  이때 묵시적으로 “/” 라우터도 등록된다.
  • /rentals URI로 접근을 위한 로직 처리를 Ember.Router 객체가 제공한는 Method를 Override하는 방식으로 처리한다. 여기에서는 템플릿에서 참조하는 데이터 모델을 제공하기 위해 model() 메소드를 Overriding했다.
  • 실제 UI를 HBS(Handlebars)를 이용해서 그린다.
  • 세부 영역의 UI 핸들링을 위해서 Component를 활용한다.

이제 각각에 대한 세부적인 부분들을 살펴보도록 하자.

App Directory Structure

Ember를 사용하는 앱 관점에서 다음과 같은 디렉토리 구조를 가지고 있다.

ember-directory

  • src/hbs – 라우터 정의에 따른 HBS 파일 경로. 파일명은 라우터의 이름과 동일해야 함.
  • src/js – Ember의 주 실행 코드 영역
    • adapter
    • components
    • controller
    • helpers
    • models
    • routers
    • app.js

이제 세부적인 각 항목의 역할을 아는 만큼 정리해본다.

Core concepts

Router

URI의 각 Path 항목들에 Mapping된 Route를 정의한다.  예를 들어 다음과 같은 URI를 살펴보자.

/family/father/job

이 부분은 다음과 같은 영역으로 구분된다.

  1. / – FamilyIndexRoute
  2. /family – FamilyRoute
  3. /family/ – FatherIndexRoute
  4. /family/father – FatherRoute

각 IndexRoute는 명시적으로 선언하지 않으면 암묵적으로 선언되며 또한 실행된다.  위 구조를 적절히 라우터로 선언하면 아래와 같은 JS 코드로 표현할 수 있다.

App.Router.map(function() {
  this.resource.route(index, { path: '/' });
  this.resource.route('family', { path: '/family' }, function() {
    this.resource.route('father');
});

평범한 거 말고 아래처럼 와일드 카드 정의도 가능하다.

Router.map(function() {
  this.route('catchall', { path:'/*wildcard' });
});

Template

Handlebars(hbs) 템플릿 언어로 기술되고 UI를 그린다.  그릴 때 필요한 데이터는 Model  혹은 Controller를 통해 정의된 속성으로 얻을 수 있다.  HBS 자체도 제어 구조를 제공하며, 이를 활용하면 간단한 반복문이나 예외 케이스등을 처리할 수 있다.

기본적으로는 HTML을 거의 동일하게 사용할 수 있으며, HBS 형식을 이용해 모델 혹은 속성을 참조할 수 있따.

  • Expression – {{firstname}} – 모델의 필드 이름을 적는다.
  • Outlets – {{outlet}} – 다른 템플릿(hbs) 파일의 내용을 outlet 이라는 공간에 들어와서 재활용 가능하도록 한다.  아래의 Index Routing에서 만약 별도의 hbs  파일을 두지 않는다면 outlet이 기본 hbs로 제공된다.

HBS 파일은 별도의 확장자를 가지기 때문에 브라우저를 통해 열어볼 수 없다.  이 부분은 디자인팀과의 협업 관점에서 마이너스 요인이 된다.

Component

Component는 HBS와 맞물려서 UI를 그릴 때 사용한다.

Model

Persistent data model, HBS에서 모델을 이용해서 화면에 내용을 출력한다.  Ember에서 손쉽게 Async ajax 호출을 위한 방안을 제공한다.

  • @each – Object 배열의 특정 element 하나를 지칭하고 싶을 때 사용한다.
  • Ember.observer – 객체내의 특정 변수의 값이 변경된 경우, 이에 대한 비동기 작업을 수행한다.

모델은 Adapter와 연계해서 Ember를 통해 동적 Ajax Request를 보낼 수 있도록 지원한다.

Controller

HBS에서 사용할 속성을 정의한다.  속성은 데이터가 될 수도 있고, 함수가 될 수도 있다.

Route

Route는 URL 접근에 따라 Controller, HBS 실행을 전/후에 실행되어야 할 JS action을 정의한다.  실행은 Route 객체의 이벤트 메소드를 Overriding하여 이뤄진다. 각 메소드의 구현체에서 model 데이터를 얻기 위해 어떤 일을 해야하는지 혹은 다른 URI 위치로 이동해야한다면 이동을 한다든지의 동작을 수행한다.

App.TicketsIndexRoute = Ember.Route.extend({
  model: function(params) {
    var slug = 'tickets';
    var activeTab = params.tab_id || null;
    var blockPromise = this.store.query('slug', {contentType: slug}).then(function(list) {
      return App.DefaultLocaleHelper.getFirstMatchingLocale(list);
    });

    return Ember.RSVP.hash({
      block: blockPromise,
      activeTab: activeTab
    });
  },
  afterRender: function() {
    $(document).foundation();
  }
});

The Application

Ember 앱이 만들어지게 되면 다음과 같이 기본 세가지 파일이 만들어진다.

{{appName}} - {{model.title}}

HBS에서의 변수 참조는 아래와 같이 처리된다.

  • Controller 속성(변수/함수) – 정의된 이름을 직접 참조
  • Model 속성 – “model.속성명”

Controller는 모델 이외에 추가로 정의된 속성을 정의한다.

 
App.ApplicationController = Ember.Controller.extend({
  appName: 'My First Example' 
}); 

라우트에서는 Model을 요청하는 메소드를 후킹해서 title 이라는 신규 속성이 내려가도록 한다.

App.ApplicationRoute = Ember.Route.extend({
  model() {
    return { title: "Hello World" };
  }
});

만약 다른 동작이 실행되길 원한다면 아래와 같이 화면 렌더링이 되기 전에 다른 곳으로 이동되도록 처리할 수도 있다.

App.LeagueIndexRoute = Ember.Route.extend({
  beforeModel: function(transition) {
    var league = this.modelFor('league');
    this.transitionTo('tournament', league.get('mainTournament.title'));
  }
});

 

Dynamic Segments – Path Variable

Path Variable을 사용해야 하는 경우에 이를 처리 하는 방법은 아래와 같다.

var Router = Ember.Router.extend();

Router.map(function(){
  this.route('post', { path: '/posts/:post_id' });
});

위와 같이 :post_id 와 같은 Path Variable을 입력으로 받아들인다면 해당 값은 아래와 같이 참조가 가능하다.

import ajax from 'ic-ajax';

export default Ember.Route.extend({
  model(params) {
    return ajax('/my-service/posts/' + params.post_id);
  }
});

 

Index Routing

만약 /favorite 같이 URI를 잡으면 Ember 내부적으로는 / 에 대한 라우팅과 /favorite에 대한 라우팅 두 개가 모두 선언된 것 같과 같다.  따라서 다음 두개의 코드는 서로 같은 의미를 가진다.

Router.map(function(){
  this.route('favorites');
});
Router.map(function(){
  this.route('index', { path: '/' });
  this.route('favorites');
});

재미있는건 /favorite으로 이동하라는 요청을 받았을 때 Ember가 내부적으로 동작은 아래의 순서를 따른다.

  • 먼저 / 에 대응하는 model, controller, hbs 를 실행한다.
  • 만약 인덱스에 대응하는 hbs가 없으면 outlet으로만 채워진 hbs가 기본으로 제공된다.
  • 그 다음에 /favorite에 대응하는 model, controller, hbs가 실행된다.

 

Advanced Features

Route class member methods for overriding

  • model()
  • setupController(controller) – controller.set(‘model’, model)를 사용해서 Controller 내부에서 참조할 값들을 셋팅할 수 있다.
  • setupController(controller, model) – this.controllerFor(‘otherController’)를 사용해서 다른 Controller의 초기화도 가능하다.
  • renderTemplate() – this.render(‘template_name’)을 활용하거나 확장해서 다른 Controller를 지정하거나 다중 Outlet을 사용할 수 있다.
  • beforeModel()
  • afterModel(posts, transition)
  • activate()

 

Component

To be summarized

Adapter

Ember에서 Adapter는 Model과 꿍짝을 이뤄 API 서버에서 데이터를 받아와 이를 Model Object 만드는 역할을 수행한다.   Ember 환경에서 API 시스템을 통한 데이터 처리는 RESTful API 규격을 따라 GET/POST/PUT/DELETE 처리에 의해 데이터의 가공이 이뤄진다.  조회 요청을 위한 파라미터의 처리는 API의 Path Variable을 통해 처리되는 것을 기본으로 한다.

 

어플리케이션 전체 수준에서 동일한 API를 사용하는 경우에는 src/js/adapter/application.js 파일에 이를 정의함으로써 처리할 수 있다.

export default DS.RESTAdapter.extend({
  namespace: 'api/1',
  host: 'https://api.example.com'
});
  • namespace – URI의 prefix에 해당 문자열을 추가한다.
  • host – API Host를 지정한다.

만약 개별 모델에 따라 이를 정의하고자 한다면 각 모델 이름 뒤에 Suffix로 Adapter를 부여한 객체를 별도로 만들면 된다.  예를 들어 League라는 모델의 경우 아래와 같이 LeagueAdapter를 만들어주면 된다.

(function (App, Ember, DS) {
  App.NavItemAdapter = DS.RESTAdapter.extend({
    namespace: 'api/v1',
    host: Riot.services.api
  });
}(App, Ember, DS));

 

Conclusion

결론은 이러하다.

EmberFlowSummary

TDD를 하신다구요?

사람들과 전화너머로 이야기를 하다보면 TDD를 자신있게 말하는 분들을 종종 만난다.  물론 이 분들의 이력서에도 “활용 가능한 기술”들 가운데 하나로 TDD라는 3글짜 알파벳이 강렬하게 적혀있다. 개인적으로 TDD 방식의 개발의 예찬론자이기도 하기 때문에 이런 분들을 만날 때마다 반가운 생각이 든다.

처음 이 단어를 들었던 때가 아마도 2010년도 쯤이었을 것 같다. 주변의 개발자들 가운데 아는 사람도 적고 해서 손에 익히기에 쉽지 않았다. 지금은 거의 대부분의 이력서에 언급될만큼 보편적인 것이 되버렸다고 생각했다.

하지만 이런 분들을 만나서 “TDD 방식으로 코드를 작성해봐주세요~” 하면 다른 양상이 펼쳐진다. 테스트가 개발을 주도하는 모습을 기대했지만 대부분 테스트는 장식이다.  이 모습을 보면서 아래와 같은 생각을 해본다.

왜 이런 의미없는 테스트를 작성할까?

테스트를 작성하는게 테스트 주도 개발인가?

뭔가 착각이 있는 것 같다. 원래 테스트와 테스트 주도 개발은 다른 건데 말이다.

시작이 문제다.

2차원상의 좌표 점들의 거리를 계산하는 프로그램을 작성해야 한다고 치자. 뭐부터 작성해야할까?

public class DistanceCalculator {
  public void addPoints(int x, int y) {
  }
  public float calc() {
    return -1;
  }
};

테스트를 먼저 한다고 하지만 그래도 이정도는 먼저 찍어놓고 해야겠지?  본능적인 촉에 의하면 2차원 좌표점이라고 이야기를 했으니까 그건 x, y로 표시해야하고, 점들이 많을테니까 이걸 관리해야하는 기능도 필요할테니 점들을 넣을 수 있는 addPoint(x, y)와 같은 메소드도 필요하다.  그리고 이것들을 가지고 계산을 해야하니까 당연히 calc() 라는 메소드도 있어야 한다.  TDD로 개발하는 개발자니까 구현은 테스트를 작성한 다음에 하는거지!!!

이렇게 하는게 과연 Test Driven일까?  하지만 이 코드는 이미 Developed 되어있다.  해야할 일은 채워넣는 일일뿐.  여기에서 테스트가 하는 일은 이미 구조가 잡힌 코드의 안정성을 보장하는데 사용된다.  물론 예외등을 테스트하다보면 코드의 발전을 이끌 수는 있겠지만, 완전한 Driven이라고 이야기는 어렵다.

테스트 먼저

TDD를 좋아하는 개발자라면 시작은 DistanceCalculator 클래스가 아니라 DistanceCalculatorTest 클래스에 대한 코드부터 적어야 한다.

public class DistanceCalculatorTest {
  @Test
  public void shouldItCalculateDistance {
  final Point start;
  final Point end;
  final float expectedDistance = 1.0f;

  GIVEN: {
    start = new Point(0, 0);
    end = new Point(0, 1);
  }

  WHEN: {
    calculator = new Calculator();
    actualDistance = calculator.distance(new Point[] { start, end });
  }

  THEN: {
    assertThat(actualDistance, is(expectedDistance));
  }
};

이 테스트 코드를 통해 우리가 많은 것들을 정리할 수 있다.

  • 계산을 수행할 객체의 이름을 정했다. DistanceCalculator 보다는 Calculator가 현재의 컨텍스트에서 좀 더 좋겠다는 생각이 들었다.  (물론 테스트의 이름도 변경하는게 맞지만 예제를 위해…)
  • 계산을 수행하기 위한 메소드는 2차원 좌표를 기술할 수 있는 Point 객체의 배열을 받는다. addPoint등을 생각할 수 있지만, 그렇게 하면 테스트 코드를 작성하는게 번거로워진다. 테스트 자체를 통해 메소드의 사용성을 평가하고 쉽게 사용할 수 있는 방향으로 메소드를 설계한다.

여기까지를 정리했다면 이제 메인 코드를 작성할 때이다.  이클립스나 IntelliJ에서 제공해주는 Code Complete 기능을 사용하면 순식간에 찍어낸다.  위 두가지 과정에서 볼 수 있는 건 우리가 작성할 코드의 방향과 형태를 테스트를 작성해봄으로써 끌어냈다라는 것이다.  이것이 Test Driven의 진정한 모습이다.

이제 실패하는 테스트를 통해 로직을 완성하면 된다.

나누고 끄집어내라

자 우리의 말썽많은 고객분께서 요구 사항을 바꿨다.  2D 세상에 만족을 못하고 3D 세상에서도 거리를 계산해달라고 한다.  테스트를 다시 한번 살펴보자.

  WHEN: {
    ...
    actualDistance = calculator.distance(new Point[] { start, end });
    ...
  }

distance 메소드가 수행하는 역할(Responsibility)를 정리해보자.

  • 인접한 두 Point 간의 거리를 계산하고,
  • 계산된 결과값의 합을 구하는 역할을 한다.

2D, 3D일지의 문제는 첫번째 “인접한 두 포인트간의 거리”에만 영향을 미친다. 나머지 기능은 원래 distance 메소드가 수행하는대로 하면 된다. 두 점 사이의 거리 계산 부분을 수행하도록 Point 객체에게 위임해버리면 된다.

한방에 끝낼려고 하지 말자

인접 점들 사이에 거리를 어떤 방식으로 distance 메소드내에서 수행되어야 할지를 결정해야 한다. 이걸 어떻게 할지를 테스트를 통해 적절한 구조와 로직을 찾아보자.

public void Space2DPointTest {
  @Test
  public void should2DPointCalculateDistnaceWithOhter() {
    final Point p1;
    final Point p2;

    GIVEN: {
      p1 = new Space2DPoint(0, 0);
      p2 = new Space2DPoint(0, 1);
    }

    final float actual;
    THEN: {
      actual = p1.distance(p2);
    }

    final float expected = 1.0f;
    THEN: {
      assertThat(expected, is(actual));
  }
}

테스트 코드를 통해 포인트에 대한 구조를 아래와 같이 잡으면 된다는걸 알 수 있다.

  • Point라는 인터페이스를 둔다.
  • 인터페이스는 Point 객체를 파라미터로 받는 distance라는 메소드를 정의한다.
  • Space2DPoint 클래스는 Point 인터페이스를 구현한다.

비슷한 맥락에서 Space3DPoint에 대해서도 테스트 클래스를 작성해보자. 두 테스트에서 동일한 구조로 동작이 가능함을 확인한다.

확인이 완료됐다면 다시 원래의 CalculatorTest 클래스로 돌아가 이를 수정한다. 현재까지 작성된 테스트들의 관점에서 우리는 GIVEN 영역을 아래와 같이 수정하면 된다.

  GIVEN: {
    start = new Space2DPoint(0, 0);
    end = new Space2DPoint(0, 1);
  }

그리고 원래의 Calculator.distance() 메소드를 아래와 같이 변경한다.

public float distance(Point[] points) {
  ...
  for (int i=1; i&amp;lt;points.length; i++) {
    distanceSum += points[i-1].distance(points[i]);
  }
  ...
 return distanceSum;
}

흠… 이 과정이 리팩토링이다. 알흠답다. ^^;

끝날때까지 끝난게 아니다.

여러분이 만드는 서비스/제품이 계속 사용자들을 만난다면 여러분의 개발은 끝난게 아니다.

변화는 필수적이며 변화에 대응하기 위한 테스트와 이에 상응하는 리팩토링 역시 지속되어야 한다.  이 과정을 반복함으로써 여러분의 코드의 날이 날카롭게 빛날 것이다.

 

ps. 조금 더 긴 프로그래머틱한 글을 이전 블로그에 올려둔 게 있다.  내용이 썩 맘에 들진 않지만 그래도 노력이 있으니까 한번 봐주길 바란다. 언젠가 기회가 된다면 이 글도 제대로 손을 봐야할 듯 싶긴 하다.

AWS를 활용한 블로그 시스템 만들기

그동안 네이버에서 제공하던 블로그를 써서 글을 써왔다.

많지 않은 글이긴 하지만 그래도 근 10년 가까이 글을 쓸 수 있도록 해준 고마운 친구였다. 하지만 변화하는 세월앞에서 그 친구도 새로운 옷을 점점 갈아입기 시작하더니 결국에는 내가 싫어하는 스타일로 가버렸다.

개발자의 이야기를 많이 하고 싶은 심정이긴 하지만 받아주는 친구가 맛집에 너무 친화적으로 바뀌다보니까 개발자스럽게 글을 쓸 수 있는 거랑은 거리가 멀어져버린 것 같다.   절이 싫으면 중이 떠나서 본인 입맛에 맞는 걸 찾는 수밖에 없겠지…  온라인 툴로 많이 사용하는 브런치도 꽤 좋은 선택지일 것 같지만 코드를 담아내기에는 최적은 아닌 것 같았다.

결론은 워드프레스(WordPress)를 이용해서 나만의 시스템을 만들자!  최근에 아마존 AWS 기반으로 일을 하고 있기 때문에 처음부터 끝까지를 해본다면 얼마나 할 수 있을지를 시험해보고 싶기도 했다. 하면 된다고 말을 했지만 실제로 처음부터 끝까지 내 손으로 해본 적은 또 없기 때문에 이번이 좋은 기회라는 생각도 들었다.

간단히 무료 블로그 시스템을 구성하는데 사용한 것들은 아래와 같다.

  • EC2 – 1 CPU, 1G Mem, 30G SSD Disk
  • Elastic IP – Public IP (고정 IP 할당용)
  • RDS – MySQL Instance, 20G 용량
  • Route53 – 도메인 등록 및 Sub domain 관리

 

Setting AWS Environment

EC2

EC2DiskUsage사용할 워드프레스는 PHP 기반이므로 아파치에서 동작된다. 이건 이걸 돌리는데 그리 큰 메모리를 필요로 하지 않는다는 것이다. 또한 특성상 CPU를 많이 먹을 이유도 없다.  그렇기 때문에 죽는걸 고려하지 않는다면 아주 간단한 웹서버만으로도 돌리는게 가능하다.

EC2에서 제공하는 Free Tier 장비로 1CPU, 1G를 제공한다. 자바 어플을 돌리기에는 부담스럽긴 하지만 워드프레스류를 돌리기에는 부담이 없다. 와중에 저장 공간 역시 30G를 제공한다. 당장 쓰는데 필요한 모든 소프트웨어를 다 깔아둔 상황임에도 불구하고 5% 수준을 사용하고 있다. 95%를 언제 채울 수 있을까 싶긴하다.

EC2를 통해 생성된 클라우드 장비는 변동 IP를 가진다. 고로 장비를 Restart되거나 혹은 리부팅되면 IP가 바뀔 가능성이 아주 농후하다.  물론 Private IP는 변경되지 않지만 Public IP는 소중한 존재이기 때문에 고정된 형태로 한 장비에 이를 허해주지는 않는다.

외부에서 그 이 장비를 어떻게 볼 수 있는거지?  그 방안을 Elastic IP를 통해 제공한다.

 

Elastic IP

AWS 환경에서 고정 Public IP를 사용하기 위해서는 Elastic IP를 사용해야 한다.  가난히 설명하면 고정 Public IP를 갖는 Domain Name을 생성하고, 그 IP에 다시 특정 EC2 Instance를 Binding 시키는 방법이다.

물론 익히 아는 바와 같이 이건 유료다. Public IP를 할당받았으면 아마존은 담달부터 당신에게 청구서를 보낼 것이다.

RDS

예전에 이런 서비스가 있다는 이야기를 듣긴 했지만 써보니 역시 좋았다. 각종 RDBMS를 내가 별도로 설치하는게 아니라 몇 개 설정만 꾹꾹 누르면 설정하기 힘든 데이터베이스 서버가 쭉쭉 생겨난다.

가장 흔하게 사용할 수 있는 MySQL 서버를 지정했다. 인스턴스 타입을 보면 Production용과 Dev/Test용 장비로 분명하게 고를 수 있다. Dev/Test 용도의 장비는 무료이기 때문에 당연히 이걸로 갔다. 와중에 용량도 최대 20G(!!)나 준다. 내가 이걸 다 채울려면 아마도 글을 정말 열심히 써야할 것 같다는 생각 뿐이다.

생성된 데이터베이스 인스턴스는 이름을 통해 접근 가능한 Entry Point를 제공한다. 물론 IP를 통해 접근도 가능하겠지만, 클라우드 환경에서 IP는 믿을 수 없는 존재이다. 어떻게든 이름을 가지고 접근할 수 있어야 한다.  설정에서 주의할 점은 MySQL 데이베이스에 대한 접근을 모든 IP 대역에서 접근하도록 허용하면 좀 곤란한다. 가급적 외부망에서의 접근은 차단하고 EC2에서 생성된 Instance만을 통해 접근이 되도록 제어를 해주는게 좋다.

Router53

도메인을 신청한다.  여기에서는 chidoo.me  물론 이것도 다 돈이다. 하지만 인터넷상에 자신의 집을 하나 가진다고 생각하면 머… 술한잔 값도 아니니 안심하구 지르시길 바란다.

도메인이 신청되면 Hosted Zone이라고 해당 도메인의 Sub domain을 관리할 수 있는 기능이 생긴다. CNAME 방식으로 Elastic IP를 통해 생성된 내부 도메인 이름을 www.chidoo.me가 되도록 설정한다.

Setting WordPress

워드프레스 설정 자체는 인터넷상에서 충분한 정보가 있기 때문에 굳이 따로 언급하지는 않는다.

다만 주의할 점은 한가지 있다. 워드프레스의 각종 기능들을 업데이트하기 위해 ftp 기능을 사용한다.  하지만 ftp는 매우 보안상 취약한 놈이기 때문에 쓰지 말아야 한다. 이 방식보다는 다음의 방법을 통해 각종 테마나 플러그인을 업데이트할 수 있다.

다음의 두 사이트에서 원하는 걸 찾는다.

  • 플러그인: https://wordpress.org/plugins/
  •  테마: https://wordpress.org/themes/browse/new/

다운로드를 원하는 것을 찾았다면 다음의 방법을 해당 모듈의 다운로드 링크를 찾는다.

GetDownloadAddress

복사를 했다면 터미널에 접속해서 이걸 플러그인 혹은 테마 디렉토리에 wget 명령을 이용해서 다운받는다.

$ cd $WPINSTALL/wp-content/

해당 디렉토리에 보면 plugins, themes 라는 디렉토리가 존재하는 것을 확인할 수 있다.


$ wget <download-url>

$ unzip <downloaded_file>

$ mv <downloaded_file>

확인 후 워드프레스의 Dashboard를 통해 활성화시키면 적용 완료!  약간의 수고를 더하면 불필요한 위험을 굳이 감내할 필요가 없다.

끝.

git: terminal에서 간지나게 써보자.

Git을 이클립스나 IntelliJ에서만 사용해야한다면 좀 쪽팔릴 것 같다.
GUI 없는 상황에서는 바보가 될 거기도 하고 뭔가 Cool하지 않다.

기억력은 역시나 3초말이라 항상 까먹는다. 흔하게 사용하는 것들 위주로 정리해놓는다.

Git Repository 처음 사용하기

$ git init
현재 디렉토리를 git 저장소로 등록한다.

$ git add –all
현재 디렉토리 및 그 하위에 있는 모든 파일들을 git의 변경 대상으로 등록한다. 하기전에 아래 .gitignore 파일에 쓸데없는 파일들을 먼저 정리하자. 안그러면 번거로운 일을 두번할 수 있다.

만약 이전에 .gitignore 파일이 없다면 아래 파일을 먼저 정의한 다음에 –all 옵션으로 실행한다. 이미 실행했다고? 그럼 rm -rf .git 명령으로 git repository 설정을 날려버리면 된다. 쫄지 말자!

.gitignore
이 파일에 만약 git을 통해 관리하기 싫은 파일이 있다면 기록해둔다. .gitignore 파일이 존재하는 디렉토리를 기준으로 상대 경로를 지정하면 해당 파일은 관리 대상에서 빠진다. 생각외로 이거 사용해보면 참 좋다. 이클립스나 IntelliJ를 포함해서 서버에 올릴 파일들을 빼고 설정을 잡는다면 대강 아래와 같다.

*.class

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# eclipse ignorings
.classpath
.project
.settings/

# intellij ignorings
*.iml
.idea/

 

변경 내용 커밋하기

$ git commit
git add 명령을 통해 등록된 파일들을 커밋한다. git add / rm 되지 않은 파일들은 커밋 대상에 포함되지 않는다. 단순히 변경했다고 하더라도. 이런 상태를 unstaging이라고 부르는데 add/rm을 통해 staged 상태로 만들어줘야 한다.

$ git commit -a
git add 명령을 통해 이전에 등록해둔 파일들의 변경(modified, deleted)된 내역들을 자동으로 커밋한다.

$ git commit file1 file2 file3
지정된 특정 파일들만 커밋한다.

$ git commit -m ‘commit message’
커밋 로그를 작성하지 않고 -m 명령에 붙은 메시지를 커밋 로그로 반영한다. 이렇게 하면 뭔가 쿨해보인다. *^^*

여기까지 했으면 로컬에 설치된 git을 통해 작업할 건 다 했다. 이제 원격에 있는 git repository에 작업된 코드를 반영할 시간이다.

$ git stash / git stash apply

작업을 하다보면 현재 작업 부분을 마무리하지 못하고 git repository에 반영을 해야만하는 경우가 있다.  작업한 부분이 좀 된다면 Rollback한 다음에 하기에는 좀 아쉬움이 크다.  그렇다고 진행중인 내용을 commit 해버릴 수도 없는 노릇이고…  이런 경우에 활용할 수 있는 명령이 stash 기능이다.  stash 기능을 사용하면 현재 작업중인 결과물을 임시로 별도의 공간에 저장하고, 소스 repository의 상태를 최종 commit한 버전의 상태로 돌려준다.

최종 커밋한 결과물까지를 서버에 push하거나 그 상태에서 추가적인 작업을 마무리하고 commit & push를 하면 된다.  그리고 stash apply 기능을 통해 이전에 작업중인 코드를 현재 branch로 불러들여와 작업을 이어가면 된다.

 

원격 서버와 친해지기

$ git remote add origin git-repo-address
원격 git repository를 현재 디렉토리에 바인딩한다. git repository를 지정하는 방식은

  • https://protocol
  • git:// or
  • user@server:path/to/repo.git

방식 가운데 하나를 사용해서 지정한다.

만약 origin 이라는 이름이 너무 상투적이라고 생각된다면 다른 이름을 사용해도 된다. 하지만 헷갈리지 말자. 지정해놓고 헷갈리면 .git 디렉토리에서 뒤지면 나온다.

$ git clone git-repo-address
만약 이미 원격에 있는 소스를 내가 받아와야 한다면, clone 명령을 사용한다. address 뒤에 별도의 디렉토리 이름을 입력하지 않으면 repository 이름이 그대로 디렉토리 이름으로 사용된다.

$ git pull origin master
원격 서버의 master branch의 변경 내역들을 땡겨온다.

$ git pull origin master
서버에 올린다. 만약 master라는 원격 branch의 이름을 까먹으면 로컬에 있는 모든 브랜치가 서버에 다 올라간다. 작업하다가 임시로 만들어놓은 것까지… 귀찮으니까 가능하면 branch의 이름을 적어주는 습관을 들이는게 좋고, 그게 아니라면 로컬에는 작업이 끝난 branch는 삭제해주는게 좋다.
평소에 보이지 않는 깔끔한 코드 작성할 때는 보여주자.

 

머지하기

다른 git repository와 머지하는 방법은 다음과 같은 방식을 사용한다.

1. 현재 작업 내용을 일단 commit 한다.

2. 현재 branch를 기준으로 머지할 브랜치를 따로 checkout 한다.
$ git checkout -b update_branch current_branch
만약 작업할 branch가 이미 있다면…
$ git checkout update_branch

3. 해당 branch로 다른 repository의 데이터를 땡겨온다.

$ git pull git-repo-address branch_name

이 과정에서 만약 쫑이나면 쫑이난 문제를 해결해야한다.
보통 쫑나면 해당 파일에 > < 와 같은 표식으로 표시가 되고, 그걸 vi 혹은 GUI 편집기를 가지고 편집하면 된다. 분량이 작을 경우에는 vi를 가지고 해도 되겠지만, 분량이 좀 된다면 그냥 이클립스나 IntelliJ를 사용하도록 하자.

4. 이제 현재 branch로 이동
$ git checkout current_branch

5. 머지하기
$ git merge update_branch

이미 앞단계에서 원격 서버와 다 합쳤기 때문에 여기서는 별달리 할게 없이 바로 되야한다. 안되면 뭔가 이상한거야!!

불필요해진 파일 삭제하기

$ git rm filename
그냥 용감하게 rm 명령으로 지운다고 git에서 제거되지 않는다. 꼭 이 명령으로 제거해줘야지 git entry에서도 빠지고, 이후에 작업하는데 번거롭지 않게된다.

채용의 방향

현재 회사의 채용 방향에 대해 가장 공감가는 글로 공유된 글이다.

혹시 외국계 소프트웨어 회사에서 프로그래머를 지망한다면 눈여겨서 볼만한 글이다.

How to pass a programming interview

굳이 요약이 필요없는 글이라 한번 일견해보는것도 좋을 것 같다.