Go 학습

Developer:Bird·2022년 3월 15일
0

Go

목록 보기
2/2

Go의 특징

1. 개발속도

  • 컴파일 시 필요한 의존성만 탐색해서 컴파일 시간이 빠름.
    동적 언어의 장점(높은 생산성) + 정적 언어의 장점(타입 안전성)을 동시에 가지게 됨

2. 동시성

  • 고루틴: 경량 쓰레드

    Go에서는 여려개의 고루틴이 하나의 스레드에서 동작
    동시성 기능 자체적으로 지원하는 net/http 라이브러리 사용

    다음과 같이 go라는 키워드 붙이면 고루틴으로 동작
go log("hello")
  • 채널: 고루틴 간의 데이터 전송위한 데이터 구조

    공유 메모리 접근하면서 생기는 문제(의도치 않는 데이터 변경) 피할 수 있다.

    데이터 교환 시 동기화가 되어 있어야지만 데이터 전달이 가능하다. 즉 Lock, Sync와 같은 작업은 필요하지 않다.
    하지만 데이터에 대해 채널을 통해 교환 하는게 아니라 포인터를 교환할 때, 여전히 동기화할 필요가 있다.

3. Go의 타입 시스템

계층구조가 없는 유연한 타입 시스템을 제공하기 때문에 리팩토링에 대한 부담을 최소화 하면서 코드 재사용가능하다. 기능을 재사용하기 위해 타입을 임베드하는 형식의 합성(composition)을 이용한다. 다른 언어들도 상속을 사용하여 composition 하지만, 의존성의 너무 강하게 연결되어 결국에는 코드 재사용이 복잡해졌지만, Go는 가볍다.

  • 간결한 타입

    내장 타입 이용은 물론 직접 타입 정의 허용하고 작은타입들을 큰타입에 임배드 하는형식으로 상속보다 단순하다.
  • 덕 타이핑

    사람이라도 오리처럼 꽥꽥울고, 오리처럼 뒤뚱뒤뚱 걸으면 그건 사람이 아니라 오리라고 판단 한다. 즉 필요한 최소 행위만 구현하면 된다. Java의 경우에는 객체지향을 따르기 위해서 수많은 인터페이스 사용해야 하고, 상속구조를 이용해야 한다.
interface User{
	public void login();
    public void logout();
}

다음과 같이 Java에서 이 인터페이스를 구현하려면 User인터페이스에 정의된 모든 약속들을 만족하는 클래스 구현 후, 그 클래스가 User 구현하고 있음을 명시적으로 선언해야 한다. 반면 Go의 인터페이스는 주로 하나의 동작만을 표현한다. 예를 들면 다음과 같다.

type User struct { // User 구조체 선언
}
type NotUser struct{ma
}
func (u User) login(){
	println("user login")
}
func (n NotUser) login(){
	println("Notuser login")
}

즉 이전 객체지향언어에서는 객체를 중심으로 구현하게 됐지만, Go는 기능을 중심으로 인터페이스를 형성 할 수 있다. 이는 코드의 재사용과 합성에서 큰 이점을 얻을 수 있다.

4. Go의 메모리 관리

  • Go만의 Garbage Collection 지원

실습

코드주소: https://github.com/webgenie/go-in-action
다음과 같은 아키텍쳐를 가진 프로젝트를 실습해보자.

main.go

package main

import (
	"chaper2/sample/search"
	"log"
	"os"
)

// init 함수는 main 함수보다 먼저 호출된다.
func init() {
	// 표준 출력으로 로그를 출력하도록 변경한다.
	log.SetOutput(os.Stdout)
}

// main 함수는 프로그램의 진입점이다.
func main() {
	// 지정된 검색어로 검색을 수행한다.
	search.Run("Sherlock Holmes")
}

seach.go

package search

import (
    "log"
	"sync"
)

// 검색을 처리할 검색기의 매핑 정보를 저장할 맵(map)
var matchers = make(map[string]Matcher)

// 검색 로직을 수행할 Run 함수
func Run(searchTerm string) {
	// 검색할 피드의 목록을 조회한다.
	feeds, err := RetrieveFeeds()
	if err != nil {
		log.Fatal(err)
	}

	// 언버퍼드 채널을 생성하여 화면에 표시할 검색 결과를 전달 받는다.
	results := make(chan *Result)

	// 모든 피드를 처리할 때까지 기다릴 대기 그룹(Wait group)을 설정한다.
	var waitGroup sync.WaitGroup

	// 개별 피드를 처리하는 동안 대기해야 할
	// 고루틴의 갯수를 설정한다.
	waitGroup.Add(len(feeds))

	// 각기 다른 종류의 피드를 처리할 고루틴을 실행한다.
	for _, feed := range feeds {
		// 검색을 위해 검색기를 조회한다.
		matcher, exists := matchers[feed.Type]
		if !exists {
			matcher = matchers["default"]
		}

		// 검색을 실행하기 위해 고루틴을 실행힌다.
		go func(matcher Matcher, feed *Feed) {
			Match(matcher, feed, searchTerm, results)
			waitGroup.Done()
		}(matcher, feed)
	}

	// 모든 작업이 완료되었는지를 모니터링할 고루틴을 실행한다.
	go func() {
		// 모든 작업이 처리될 때까지 기다린다.
		waitGroup.Wait()

		// Display 함수에게 프로그램을 종료할 수 있음을
		// 알리기 위해 채널을 닫는다.
		close(results)
	}()

	// 검색 결과를 화면에 표시하고
	// 마지막 결과를 표시한 뒤 리턴한다.
	Display(results)
}

// 프로그램에서 사용할 검색기를 등록할 함수를 정의한다.
func Register(feedType string, matcher Matcher) {
	if _, exists := matchers[feedType]; exists {
		log.Fatalln(feedType, "검색기가 이미 등록되었습니다.")
	}

	log.Println("등록 완료:", feedType, " 검색기")
	matchers[feedType] = matcher
}
profile
끈임없이 발전하자.

0개의 댓글