JavaScript 개발자를 위한 Golang - 1부(번역)
Golang for JavaScript developers - Part 1
출처
Golang for JavaScript developers - Part 1
시작
만약 당신이 다른 프로그래밍 언어를 배우는 것에 대해 생각하고 있는 Javascript 개발자라면, Golang은 훌륭한 선택이다. 단순하며 많은 모멘텀과 좋은 성능 그리고 JavaScript와 일부 유사하다.
Edit: 어떤 사람이 댓글에서 JS 개발자가 가능한 모든 옵션 중에서 왜 Go를 선택해야 하는지 물었다. 내 생각에, JS는 완벽한 언어가 아니기 때문에 다른 언어를 배우면 JS 개발자가 JS를 더 실용적으로 사용할 수 있게 큰 도움이 될 것이고 기본적인 프로그래밍 개념에 대한 그녀/그의 지식을 강화하는 데도 도움이 될 것이다. 물론 Rust, Go, Haskel, Kotlin 등과 같은 많은 옵션들이 있지만, 나는 Go가 사용 가능한 모든 옵션 중에서 가장 단순하고 널리 채택되어 시작하기에 좋은 것이라고 생각한다. 나의 두번째 선택은 Kotlin이나 Rust가 될 것이다.
이 포스트는 언어를 비교하는 것이 아니고 그들이 매우 유사하다고 말하고 있다. 이것은 JavaScript 개발자가 Golang을 빨리 파악할 수 있는 가이드이다. Go에는 우리가 다룰 자바스크립트와 완전히 다른 많은 측면들이 있다.
더 유사한 것들
Go에는 JavaScript의 개념과 매우 유사한 것들이 많이 있다. 대부분은 같지 않으나 비슷하다. 먼저 우리는 그것들을 알아보는 것에 힘쓰자. 이 시리즈의 1부에선, 우리는 그것들이 어떻게 비슷한 지 살펴보고 주요 차이점도 주목할 것이다.
Functions
JS와 Go에서 가장 유사한 특징은 functions이다.
유사점
- Functions는 일급 객체이다.
- Functions는 변수에 할당할 수 있다.
- Functions는 인수로 다른 functions을 전달할 수 있고, functions으로 반환할 수 있다.
- Functions는 중첩 될 수 있다.
- Functions는 커리 될 수 있다. (일부 기능)
- Functions는 주변 컨텍스트를 기억해서 클로저를 만들 수 있다.
- Functions는 이름을 지정하거나 익명으로 지정할 수 있다. 익명 functions를 즉시 호출 할 수 있다.(IIFE)
Javascript
//`this`에 접근 할 수 있는 보통 function
function standardFunction(arg1, arg2) {
return `${arg1}:${arg2}`;
}
// 변수에 할당 된 function
const assignedFunction1 = standardFunction;
// 변수에 할당 된 화살표 function
const assignedArrowFunction = (arg1, arg2) => {
return `${arg1}:${arg2}`;
};
// function를 인수로 받아들이고 function를 반환하는 고차 function
function functionAsArgumentAndReturn(addFn, arg1, arg2) {
const out = addFn(arg1, arg2);
// 클로저를 반환
return function(numArg) {
return out + numArg;
};
}
const out = functionAsArgumentAndReturn(
(a, b) => {
return a + b;
},
5,
10
)(10);
// returns 25
// 중첩 functions
function nested() {
console.log("outer fn");
function nested2() {
console.log("inner fn");
const arrow = () => {
console.log("inner arrow");
};
arrow();
}
nested2();
}
nested(); // prints:
// outer fn
// inner fn
// inner arrow
// 이것은 function을 반환하는 고차 function이다.
function add(x) {
// function은 클로저로 반환된다.
// 변수 x는 이 방법의 외부 스코프에서 얻어지고 클로저에 기억된다.
return y => x + y;
}
// 더 많은 커링을 만들기 위해 add 메소드를 사용하고 있다.
var add10 = add(10);
var add20 = add(20);
var add30 = add(30);
console.log(add10(5)); // 15
console.log(add20(5)); // 25
console.log(add30(5)); // 35
// 즉시 호출 된 익명 function (IIFE)
(function() {
console.log("anonymous fn");
})();
// prints: anonymous fn
Go
// 보통 function, 이 function은 중첩 될 수 없다
func standardFunction(arg1 string, arg2 string) string {
return fmt.Sprintf("%s:%s", arg1, arg2)
}
func main() {
// 변수에 할당 된 function
var assignedFunction1 = standardFunction
// 변수에 할당되고 중첩 된 익명 function
var assignedFunction2 = func(arg1 string, arg2 string) string {
return fmt.Sprintf("%s:%s", arg1, arg2)
}
// function을 인수로 받아들이고 function를 반환하는 고차 function
var functionAsArgumentAndReturn = func(addFn func(int, int) int, arg1 int, arg2 int) func(int) int {
var out = addFn(arg1, arg2)
// 클로저를 반환
return func(numArg int) int {
return out + numArg
}
}
var out = functionAsArgumentAndReturn(
func(a, b int) int {
return a + b
},
5,
10,
)(10)
fmt.Println(out) // prints 25
// 중첩 된 익명 functions
var nested = func() {
fmt.Println("outer fn")
var nested2 = func() {
fmt.Println("inner fn")
var nested3 = func() {
fmt.Println("inner arrow")
}
nested3()
}
nested2()
}
nested() // prints:
// outer fn
// inner fn
// inner arrow
// function를 반환하는 고차 function
var add = func(x int) func(y int) int {
// function은 클로저로 반환된다.
// 변수 x는 이 방법의 외부 스코프에서 얻어지고 클로저에 기억된다.
return func(y int) int {
return x + y
}
}
// 더 많은 커링을 만들기 위해 add 메소드를 사용하고 있다.
var add10 = add(10)
var add20 = add(20)
var add30 = add(30)
fmt.Println(add10(5)) // 15
fmt.Println(add20(5)) // 25
fmt.Println(add30(5)) // 35
// 즉시 호출 된 익명 function (IIFE)
(func() {
fmt.Println("anonymous fn")
})()
// prints: anonymous fn
assignedFunction1("a", "b")
assignedFunction2("a", "b")
}
차이점
- JavaScript Functions에는 두가지 형태가 있다; 정규 functions, 그리고 화살표 functions 반면에 Go 에는 보통 functions 와 인터페이스 functions이 있다. 보통 Go functions는
this
를 가지지 않는다. 따라서 화살표 functions과 더 유사한 반면에 인터페이스 functions는this
와 비슷한 것이 있다. 이런 이유로 JavaScript의 보통 functions에 가깝다. Go에는 글로벌this
라는 개념이 없다.
Javascript
function normalFnOutsideClass() {
console.log(`여전히 글로벌 this에 접근할 수 있다: ${this}`);
}
const arrowFnOutsideClass = () => {
console.log(`this를 가지고 있지 않다`);
};
class SomeClass {
name = "Foo";
normalFnInsideClass = function() {
console.log(`this로 class에 접근할 수 있다: ${this.name}`);
};
arrowFnInsideClass = () => {
console.log(`this로 class 참조에 접근할 수 있다: ${this.name}`);
};
}
new SomeClass().normalFnInsideClass();
new SomeClass().arrowFnInsideClass();
Go
type SomeStruct struct {
name string
}
func (this *SomeStruct) normalFnInsideStruct() {
// 변수 이름을 this 또는 다른것으로 지정할 수 있다
fmt.Printf("this로 struct 참조에 접근할 수 있다\n: %s", this.name)
}
func main() {
var normalFnOutsideStruct = func() {
fmt.Println("내 스코프의 변수에 접근할 수 있다")
}
normalFnOutsideStruct()
var structVal = SomeStruct{"Foo"}
structVal.normalFnInsideStruct()
}
- JavaScript functions는 다른 값 유형과 동일하므로 Go에서 불가능한 추가 속성을 보유 할 수도 있다.
- Go functions는 암시적으로 명명된 반환값(implicit named returns)을 가질 수 있다.
- Go에는 익명 functions만 중첩 될 수 있다.
- Go functions는 여러 값을 반환할 수 있지만 JavaScript에서는 한 값만 반환할 수 있다. 그러나 JS에서 구조 분해(destructuring)를 사용해서 문제를 해결할 수 있으니 양쪽 모두에서 비슷한 모양의 functions를 만들 수 있다.
Javascript
function holdMyBeer() {
return ["John", 2];
}
let [a, b] = holdMyBeer();
console.log(`Hey ${a}, hold my ${b} beer\n`);
Go
func holdMyBeer() (string, int64) {
return "John", 2
}
func main() {
a, b := holdMyBeer()
fmt.Printf("Hey %s, hold my %d beer\n", a, b)
}
스코프
스코프는 변수가 유효한 컨텍스트로 변수를 사용할 수 있는 위치를 결정하며 JS와 Go 둘 모두 많은 유사점을 가지고 있다.
유사점
- 둘 다 function 스코프를 가지며 Functions는 주변 스코프를 기억할 수 있다.
- 둘 다 block 스코프를 가지고 있다.
- 둘 다 글로벌 스코프를 가지고 있다.
차이점
- Go에는 JavaScript에서 까다로운 개념인
this
라는 개념이 없다. 내 생각에 이것은 Go에서 일을 훨씬 더 간단하게 만든다. - Go에서 같은 스코프의 변수는 다시 선언할 수 없다. Go의
var
는 JS에서let
키워드에 더 가깝다.
흐름 제어(Flow control)
Golang의 흐름 제어는 JavaScript와 매우 유사하지만 많은 면에서 더 단순하다.
유사점
for
루프는 둘 다 매우 유사하다.while
루프는 매우 유사하지만 Go는 동일한for
키워드를 사용한다.forEach
도 기능면에서 비슷하지만 구문은 상당히 다르다.- 루프에서 break/continue를 할 수 있다. labels을 사용하여 그렇게 할 수도 있다.
if/else
문법은 상당히 비슷하고. Go 버전이 좀 더 강력하다.
JavaScript
// For loop
for (let i = 0; i < 10; i++) {
console.log(i);
}
// While loop
let i = 0;
while (i < 10) {
console.log(i);
i++;
}
// Do while
let j = 0;
do {
j += 1;
console.log(j);
} while (j < 5);
// ForEach loop
["John", "Sam", "Ram", "Sabi", "Deepu"].forEach((v, i) => {
console.log(`${v} at index ${i}`);
});
// for of loop
for (let i of ["John", "Sam", "Ram", "Sabi", "Deepu"]) {
console.log(i);
}
// For in loop
const obj = {
a: "aVal",
b: "bVal"
};
for (let i in obj) {
console.log(obj[i]);
}
Go
func main() {
// For loop
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// While loop
i := 0
for i < 10 {
fmt.Println(i)
i++
}
// Do while
j := 0
for {
j += 1
fmt.Println(j)
if j == 5 {
break
}
}
// ForEach and for of loop
for i, v := range []string{"John", "Sam", "Ram", "Sabi", "Deepu"} {
fmt.Printf("%v at index %d\n", v, i)
}
// For in loop
var obj = map[string]string{
"a": "aVal",
"b": "bVal",
}
for i, v := range obj {
fmt.Printf("%v at index %s\n", v, i)
}
}
차이점
- Go에는 삼항 연산자(ternary operator)가 없다.
switch
구문은 비슷하지만 Go는 기본적으로 중단되고 JS는 기본적으로 통과한다.- Go에서는 그 기능에 대해
fallthrough
키워드를 사용할 수 있고, JS에서는break
키워드를 사용할 수 있다. - JS에는
while
,forEach
,for in
&for of
루프와 같은 더 많은 반복 방법이 있으며, Go에서는 사용할 수 없으나 대부분 for 구문을 사용하여 달성 할 수 있다. if/else
는 Go에서 초기화 할당을 가질 수 있다. 아래 코드에서val
에 대한 할당은if
와else
블록 내에서만 스코프를 가지며 그 외부에는 적용되지 않는다. JS에서는 불가능하다.
Go
if val := getVal(); val < 10 {
return val
} else {
return val + 1
}
메모리 관리
메모리 관리도 JS와 Go의 세부 사항을 제외하고는 매우 유사하다.
유사점
- 둘 다 런타임에 가비지 컬렉션이 된다.
- 둘 다 힙 및 스택 메모리를 가지며 이는 둘 다 동일하다.
차이점
- Go는 사용자의 메모리 관리를 추상화하는 동안 사용자에게 노출되는 포인터를 가지고 있는 반면에 JavaScript 포인터는 완전히 추상화되어있고 값과 참조로만 작업한다.
- Go는 대기시간에 초첨을 맞춘 concurrent tricolor mark-and-sweep 알고리즘을 사용하지만 JS 엔진은 일반적으로 Mark-Sweep이 널리 사용되는 다른 알고리즘을 구현한다. 예를 들어 V8 엔진은 Mark-Sweep과 Scavenge 알고리즘 둘다 사용한다.
Misc
- 주석은
//
및/ * * /
로 동일하다. - JS와 Go 모두 다른 모듈 가져 오기를 지원하지만 동작은 같지 않다.
- SetTimeout은 둘 다 비슷하다.
setTimeout(somefunction, 3*1000)
vstime.AfterFunc(3*time.Second, somefunction)
. - 둘 다 스프레드 연산자가 있다
console.log (... array)
vsfmt.Println (array ...)
. Go 스프레드는 인터페이스 arrays/slices에서만 작동한다. - 둘 다 메소드 인수에 나머지 연산자가 있다
...nums
vsnums ...int
.
결론
1부에서 우리는 두 언어에서 비슷한 개념을 살펴보았다. 이 시리즈의 다음 편에는 JS와 Go의 더 다른 점을 알아볼 것이다. 다음 편에는 이것보다 다른 것들이 더 있지만 몇 가지 차이점들은 미묘해서 JavaScript 개발자는 이해하기 쉬울 것이다.
다음 편에서 살펴볼 것:
- Types & Variables
- Error handling
- Mutability
- Composition instead of inheritance
- Concurrency
- Compilation
- Paradigm
참조:
- http://www.pazams.com/Go-for-Javascript-Developers/
- https://github.com/miguelmota/golang-for-nodejs-developers
마치며
부족한 영어 실력이나마 매끄럽게 번역을 하기 위해 직역 및 의역을 다수 사용했다.
(문장의 해석이 오역이거나 수정이 필요한 점이 있다면 언제든 알려주시면 반영하겠습니다.)
- function은 함수라 표기하지 않고 영문 그대로 표기했다.
- 같거나 비슷한 뜻으로 쓰인 것 같으나 구분을 위해 normal function은 보통 function, regular function은 정규 function으로 번역했다.