2022-09-13
new(T)
vsMake(T)
new(T)
allocates zeroed storage for a new item of type T and returns its address as a value of type *Tmake(T)
returns an initialized value of type T.- For slices, maps and channels: use
make
- For arrays, structs and all value types: use
new
- Go Best practices
- value type에 대한 pointer를 misuse하지 말기, 이는 stack에 있는 값을 heap영역으로 복사하게 되면서 추가적으로 메모리 할당이 일어나게 된다.
- 변수이름은 구현체가 안드러나도록, 추상화레벨에 맞추는것이 좋다.(eg. redisPubSub대신 그것을 추상화한 broadcastQueue)
- sync.map vs map + mutex
- sync.map : 동기화에 대한 부담이 줄어들지만 key,value의 타입을 명시적으로 선언해주지 못한다는 단점존재
- map과 mutex 사용 : key,value의 타입을 명시적으로 지정가능, 동기화에 대한 부담 가중
2022-09-16
- 코드 리뷰중 배운사항
- 새로운 파일을 git에 추가시, 파일의 맨마지막에 newline을 반드시 추가하는것이 컨벤션이다. 이렇게 해야하는 이유는 다음에 파일을 수정할때 newline을 입력하면서 diff에 newline입력부분이 포함되어 지저분해지기 때문이다. GoLand에서는 파일 저장시 자동으로 newline을 삽입하도록 설정할수 있다.(2022.2 버전기준으로 Preferences -> Editor -> General -> OnSave 하단에 Ensure every file ends with a line break)
- 윈도우에서는 newline문자를 crlf를 사용하고 unix/linux에서는 lf를 사용한다. 만약 윈도우에서 작업한 파일을 클론하여 수정하면 crlf 문자를 lf로 변환하는 불필요한 diff가 발생하게 된다. 이를 해결하기 위해서
core.autocrlf
설정을 세팅해야 한다. 아래와 같은 옵션이 있는데 mac의 경우 input으로 설정하고 windows의 경우 true로 설정해야 한다.- core.autocrlf = false. 기본 설정이다. 파일에 CRLF 를 썼든 LF 를 썼든 git 은 상관하지 않고 파일 그대로 checkin, checkout 한다. 이 설정은 line ending 이 다른 OS 에서는 text file 이 변경되었다고 나오므로 위에서 언급한 여러 가지 문제가 발생할 수 있다.
- core.autocrlf = true text file을 object database 에 넣기전에 CRLF 를 LF 로 변경한다.
- core.autocrlf = input LF를 line ending 으로 사용한다.
- git commit 메시지 작성요령 :
https://meetup.toast.com/posts/106
- 여기서 명령형으로 하는 이유가 git에서 자동생성된 커밋과 위화감 없게 하기위함 이라니 신기했다.
- 커밋 메시지의 첫문자는 항상 대문자, 명령형으로 시작하기!
- 동료의 개발환경 세팅을 도와주던중
protoc output: protoc error: fork/exec /usr/local/bin/protoc: exec format error
라는 에러를 보게되었는데 이것을 보고 protoc 실행에 문제가 있다고 의심을 했어야 했다. 그런데 m1 mac이라서 protoc가 제대로 동작하지 않을거라고 망상했다. 소경이 소경을 인도할수는 없다. 항상 글자 그대로 받아들이는 연습을 해야겠다. - context.Context 패키지 cancelFunc 함수가 여러번 호출되는 경우, 어떻게 동작할까 궁금해서 구글링을 먼저했는데 답이 잘 나오지 않았다. 내부 코드에 있는 주석을 보고나서 바로 파악할 수 있었다. 항상 쉬운 방법을 고집할것이 아니라 가장 정석적인 방법도 염두에 두어야 겠다.
Go lang에서 파라미터에 인터페이스 타입을 포인터로 정의해둘지 여부에 대한 따른 고찰
- 일반적으로 인터페이스 타입을 포인터 타입으로 정의해둘 필요는 거의 없다.
- 파라미터로 넘길 struct에서 value 리시버로 메소드를 정의해둔 경우 값으로 넘기든 포인터로 넘기든 상관없다.
- 반면 포인터 리시버로 메소드를 정의해둔 경우, 함수 파라미터 타입이 값 인터페이스 타입이더라도 반드시 struct의 주소를 넘기도록 강제하고 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main
type Iface interface {
foo()
}
var _ Iface = (*Impl1)(nil)
type Impl1 struct {
mp map[string]string
}
func (i Impl1) foo() {
}
var _ Iface = (*Impl2)(nil)
type Impl2 struct {
mp map[string]string
}
func (i *Impl2) foo() {
}
func baz() {
i1 := Impl1{}
boo(i1)
boo(&i1)
i2 := Impl2{}
boo(i2) // compile error가 난다.
boo(&i2)
}
func boo(iface Iface) Iface {
_ = iface
}
- slice의 경우 새로운 아이템을 추가할때 새로운 array가 할당되는데, 같은 array가 할당되게 하려면 아래 두가지 방법을 사용할수 있다.
1
2
3
4
5
6
7
func foo1(s *[]string) {
*s = append(*s, "ss")
}
func foo2(s []string) []string {
return append(s, "ss")
}
2022-09-15
- GoLand에서 code inspection 기능을 사용하면 채널 close나 context.cancel() 함수 처럼 빠뜨리기 쉬운 작업들이 제대로 되었는가 체크할수 있다.
- go의 convention상 context.Context 타입의 입력 파라미터는 항상 맨 앞에 와야하고 error 타입의 파라미터는 맨뒤에 와야 한다.
2022-09-21
- error 로그를 찍을때 상세한 정보 추가하기
- grpc에서 클라이언트로 에러를 내려줄때 응답코드 내려주기
- grpc 핸들러에서 코드는 ans struct 생성, 작업, 결과 리턴 패턴으로 작성
2022-09-23
blocking vs non-blocking
- Blocking
- A 함수가 B 함수를 호출 할 때, B 함수가 자신의 작업이 종료되기 전까지 A 함수에게 제어권을 돌려주지 않는 것
- Non-blocking
- A 함수가 B 함수를 호출 할 때, B 함수가 제어권을 바로 A 함수에게 넘겨주면서, A 함수가 다른 일을 할 수 있도록 하는 것.
Synchronous vs Asynchronous
- Synchronous
- A 함수가 B 함수를 호출 할 때, B 함수의 결과를 A 함수가 처리하는 것.
- Asynchronous
- A 함수가 B 함수를 호출 할 때, B 함수의 결과를 B 함수가 처리하는 것.
2022-09-30
- server repo pr 올릴때 관련된 다른 pr 링크도 description에 명시할것
- 영역구분 등과 같이 의미가 적은 주석 사용하지 않기
- 추후에 수정해야하는 부분은
TODO
,FIXME
로 시작하는 코멘트 추가 또는 해당 라인에 pr comment 달기 - RWLock의 경우, Reader가 락을 잡고 있는 동안 다른 Reader가 CS에 진입이 가능하고 다른 Writer는 Reader가 unlock할때까지 대기한다. Writer가 락을 잡으면 그 동안 Reader는 대기한다.(참고)
2022-10-24
- Go에서 pointer interface 타입은 사용하지 않는다. 인터페이스 타입은 인터페이스에 저장된 타입정보에 대한 포인터와 연관된 데이터에 대한 포인터를 저장하기 때문이다. 참고
2022-11-08
- Fake, mock, stub 차이점
- Fake object : 실제로 동작하는 구현이지만 production에는 suitable하지 않은 것입니다.
- Mock object : 실제 객체를 모방하여 테스트용으로 작성한 특수 케이스 객체
- Stub : 미리 준비된 응답만 제공하기 위해 테스트용으로 작성한 것. 즉, 반환값만 맞춰 준 것.
- Spy : Stub이긴 한데 호출 방식에 따른 정보 기록(record) 까지 수행
- Uber Go Style Guide
2022-11-11
- Mac 업데이트후 몇몇 프로그램 설치가 안되있는경우 설치방법
- diff 설치 :
brew install diffutils
- realpath 설치 :
brew install coreutils
- diff 설치 :
2022-11-14
- Makefile시 .PHONY 사용하는 이유
- target과 실제 파일 이름이 동일할시 기본은 파일에 있는 커멘드를 실행한다. 하지만 .PHONY에 명시할경우 target이 실행된다.
- 하위 make에서 에러가 발생할때 커멘드를 중지, 커멘드를 병렬로 실행할수 있도록 제공
- Mac에서 구름입력기 설치하여 폰트 한글 입력속도 개선하기
- https://github.com/gureum/gureum/wiki/Help-Install
- Unix File Permissions
- 1번째 비트 : 디렉토리면 “d” 아니면 “-“
- 2,3,4번째 비트 : file의 user(owner)에 대한 read,write,execute permission
- 5,6,7번째 비트 : group에 대한 read,write,execute permission
- 8,9,10번째 비트 : other(world)에 대한 read,write,execute permission
2022-12-06
- Go에서 map을 초기화할때 가능하면 아래와 같이 capacity hint를 제공하는것이 좋다.
- slice와 달리 complete, preemptive allocation을 보장해주지는 못하지만 필요한 hashmap bucket의 수를 approximate하는데 사용된다.
- Slice을 초기화할때 Capacity를 가능하면 명시하기
- capacity를 명시해두면 compiler가 slice capacity를 위한 충분한 메모리를 할당해주며, slice 길이가 capacity에 도달하기 전까지는 연속적인 append작업시 추가적인 메모리 할당을 유발하지않는다.
2022-12-07
- Uber Go Style Guide 문서를 1회독 했다. 주기적으로 읽으면 좋을것같다.
2022-12-13
- Golang에서 reference variable은 없음
- map타입은 내부적으로
func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap
함수의 리턴값이며, 따라서 hmap에 대한 pointer이다. 즉 hmap에 대한 주소값을 담은 uintptr를 리턴할 뿐이다. 리턴한 값에 새로운 map을 할당한다고 해서 원래 pointer가 가리키고 있던 map에는 아무 영향이 없다. - map타입은 pointer 타입인데 왜 *map으로 정의하지 않는가?
- *map[int]int 형태로 정의할 경우,map을 사용하는 사람들이 *map 이외의 형태로 사용하는 사람은 없기때문에, *map[타입]타입을 map[타입]타입 으로 리네임한것 뿐이다.
- map타입은 내부적으로
2022-12-20
- 만약 길이가 0인 slice를 선언한다면 make나 []int{}로 정의하기 보다는
var nums []int
로 하는것이 낫다. - 항상 할당과 동시에 선언할수 있으면 하는것이 낫다.
2023-01-13
go mod download
go.mod
파일을 읽어서 모듈을 다운받기만 한다.
go mod tidy
- package graph를 순회하고
go.mod
파일에서 빌드에 필요한 부분만 남기고 나머지는 정리한다.
- package graph를 순회하고
go get
- package 다운로드 이후에 install
go install
- 다운로드된 모듈 install
2023-01-26
- 네이티브 안드로이드 개발자 입장에서의 gRPC장단점
- 장점
- 속도향상
- 코드 제너레이션
- 단점(+ 회의적인 부분)
- 추가 학습 필요
- 안드로이드 스튜디오에서 지원하는 gRPC 라이브러리들이 안정적이지 않음
- 안드로이드 스튜디오 업데이트시 기존의 gRPC 라이브러리를 지원하지 않음
- 애초에 큰 데이터를 받아오지 않는 네이티브 앱의 경우, 속도 향상폭이 크지않음, 따라서 gRPC+RestAPI 하이브리드로 가는것도 좋음
- 장점
2023-02-01
- Github에서 compare기능 사용하여 두 repo 버전 비교
- 변경된 파일 리스팅: github.com/[organization]/[repo name]/compare/[branch or commit sha]..[branch name]
- 변경된 커밋이력 리스팅: github.com/[organization]/[repo name]/compare/[branch or commit sha]…[branch name]
- 자세한 설명 : https://docs.github.com/en/pull-requests/committing-changes-to-your-project/viewing-and-comparing-commits/comparing-commits
- 주로 코드 리뷰에서 특정 부분커밋만을 보여주고 싶을때 유용할것 같다.
2023-02-08
- brew 커멘트 실행시
cannot load such file -- active_support/core_ext/object/blank
에러가 발생하면brew update-reset
실행
2023-03-12
Mac에서 배치로 스크립트 실행하기
아래는 5분마다 특정 경로의 shell 스크립트 실행하는 예제임. /Users/{유저네임}/Library/LaunchAgents
디렉토리에 sdm.simple.TerminateDisplaylink.plist
파일을 생성하고 파일 내용은 아래로 설정(sh 스크립트는 Desktop이나 Download 디렉토리에 위치시키면 실행할 권한이 없으므로 위치하지 않도록 유의!)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.terminate_displaylink</string>
<key>ProgramArguments</key>
<array>
<string>/Users/{유저네임}/terminate_displaylink.sh</string>
</array>
<key>StartInterval</key>
<integer>300</integer>
</dict>
</plist>
2023-03-16
- Jetbrains IDE에서 하단의 tool window bar 활성화하는 법 : Preferences -> Appearance & Behavior -> Appearance 탭에서 Tool Windows 섹션에
Show tool window bars
체크
2023-03-30
- Goland에서 interface implementation으로 이동하기 :
cmd + option + B
2023-04-01
- https://restfulapi.net/ 문서에 따르면 RESTful 하다고 하면 많이들 HTTP Method와 연관짓는 경우가 많은데, 실제로 이는 관련없다. 중요한건 uniform interface이다.
- REST vs RESTful
- 참고 글
- Cacheability
- REST의 경우 cacheable한 리소스와 그렇지 않은 리소스를 구분한다.
- 반면 RESTful API의 경우에는 모든 리소스가 cacheable하다.
- Stability
- REST에서 서바와 클라이언트는 서로의 상태정보를 저장하지 않는다.
- RESTful에서는 서버와 클라리언트는 서로 데이터와 상태를 주고 받는다.
- REST에서는 분명 stateless라고 했는데, 이러면 REST 인터페이스를 준수한건지 솔직히 잘 모르겠다. 이래도 과연 RESTful하다고 할수 있는지..?
- Multi-layered architecture
- REST에서는 클라이언트가 직접 소통하는 레이어 너머를 알수 없다.
- RESTful에서는 클라이언트가 여러 레이어와 소통가능하다.
2023-04-03
go get
은 디폴트로 아래와같이 동작- import path가 github.com으로 시작하면 해당 패키지rk github.com 호스팅과 git vcs를 사용한다고 판단하며
https://
와git+ssh://
스키마 순서로 다운로드를 시도한다. https://proxy.golang.org
에서 미러링된 모듈 검색- 모듈을 발견하면 다운받고
go.sum
파일에서 체크섬 유무 검사 go.sum
업데이트
- import path가 github.com으로 시작하면 해당 패키지rk github.com 호스팅과 git vcs를 사용한다고 판단하며
- private module의 경우는 퍼블릭 서버에 없으므로
go get
이 실패한다. 따라서 위 과정을 우회하기 위해서 GOPRIVATE 환경변수 등록 - github action에서 접근하도록 할때, ssh보다 access token을 통한 접근이 일반적이므로 다음과 같이 git config로 url만 변경
$ git config --global url."https://x-access-token:$@github.com/".insteadOf "https://github.com/"
- 6 Ways To Boost the Performance of Your Go Applications
- application들을 쿠버네티스에서 실행한다면,
GOMAXPROCS
을 리눅스 컨테이너 CPU quota와 매치되도록 설정 - struct fields 정렬하기
- Garbage collection tune
- use only
GOMEMLIMIT
to trigger Garbage Collection.
- use only
- Use unsafe package to string <-> byte conversion without copying
- Use jsoniter instead of encoding/json
- Use sync.Pool to reduce heap allocations
- application들을 쿠버네티스에서 실행한다면,
- Makefile
@
sign in the beginning of the recipe- means
do not echo the command
- means
2023-04-04
- GoLand에서 Save시 Reformat 자동화하기
- Preferences -> Actions On Save -> Reformat code에 Files 버튼눌러서 Reformat하고자 하는 파일 확장자 선택
2023-04-12
- Find all files in current and subfolders based on wildcard matching
- ` find
-type f -name " " ` - ` find . -name “*.proto” `
- 참고
- ` find
- assign command result to variable
VARIABLE_NAME = $(shell find <driectory_path> -iname "*.proto")
2023-04-20
- 항상 코딩할때 포인터가 nil일수 있다는것을 염두해두자.
- 항상 값이 비어있을수 있다는 점을 염두해두자!
2023-04-21
- 자주 놓치는 부분
- 데이터를 저장할때와 읽을때 key를 다르게 사용하는것
- 로직 처리가 제대로 안되어있는 부분
2023-05-02
errors.As
vserrors.Is
- errors.Is는 error를 구현한 타입의 값비교, errors.As는 err가 target으로 assignable하거나 As함수가 true를 반환하는지 체크
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
type MyErrorImplementedByPointerReceiver struct {
s string
}
func (m *MyErrorImplementedByPointerReceiver) Error() string {
return m.s
}
type MyErrorImplementedByValueReceiver struct {
s string
}
func (m MyErrorImplementedByValueReceiver) Error() string {
return m.s
}
func TestErrorCheck(t *testing.T) {
var err error
/* errors.As 함수의 target에 concrete type를 넘길경우, err와 target의 타입이 정확히 일치해야한다.
즉, err가 pointer이면 concrete type도 pointer이어야하고, err가 value이면 concrete type도 value이어야한다.
*/
err = MyErrorImplementedByValueReceiver{}
concreteErr := MyErrorImplementedByValueReceiver{}
assert.True(t, errors.As(err, &concreteErr))
err = MyErrorImplementedByValueReceiver{}
concreteErr2 := &MyErrorImplementedByValueReceiver{}
assert.False(t, errors.As(err, &concreteErr2))
err = &MyErrorImplementedByValueReceiver{}
concreteErr3 := &MyErrorImplementedByValueReceiver{}
assert.True(t, errors.As(err, &concreteErr3))
err = &MyErrorImplementedByValueReceiver{}
concreteErr4 := MyErrorImplementedByValueReceiver{}
assert.False(t, errors.As(err, &concreteErr4))
/* compile error
second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface typesecond argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type
err = &MyErrorImplementedByPointerReceiver{}
concreteErr5 := MyErrorImplementedByPointerReceiver{}
assert.True(t, errors.As(err, &concreteErr5))
*/
err = &MyErrorImplementedByPointerReceiver{}
concreteErr6 := &MyErrorImplementedByPointerReceiver{}
assert.True(t, errors.As(err, &concreteErr6))
/* 아래 처럼 target을 error interface 타입으로 넘기면 값으로 넘기든 포인터로 넘기든 상관없다. */
var err2 error
err = MyErrorImplementedByValueReceiver{}
err2 = MyErrorImplementedByValueReceiver{}
assert.True(t, errors.As(err, &err2))
err = MyErrorImplementedByValueReceiver{}
err2 = &MyErrorImplementedByValueReceiver{}
assert.True(t, errors.As(err, &err2))
err = &MyErrorImplementedByValueReceiver{}
err2 = &MyErrorImplementedByValueReceiver{}
assert.True(t, errors.As(err, &err2))
err = &MyErrorImplementedByValueReceiver{}
err2 = MyErrorImplementedByValueReceiver{}
assert.True(t, errors.As(err, &err2))
err = &MyErrorImplementedByPointerReceiver{}
err2 = &MyErrorImplementedByPointerReceiver{}
assert.True(t, errors.As(err, &err2))
/* errors.Is 함수에는 두개의 파리미터중 포인터가 있으면 실제 같은 인스턴스인지 체크하고, 그렇지 않으면 값을 비교한다. */
test := MyErrorImplementedByValueReceiver{}
test.s = "test"
err = test
concreteErr7 := MyErrorImplementedByValueReceiver{}
concreteErr7.s = "test"
err2 = concreteErr7
assert.True(t, errors.Is(err, err2))
test = MyErrorImplementedByValueReceiver{}
test.s = "test1"
err = test
concreteErr8 := MyErrorImplementedByValueReceiver{}
concreteErr8.s = "test"
err2 = concreteErr8
assert.False(t, errors.Is(err, err2))
test2 := &MyErrorImplementedByPointerReceiver{}
test2.s = "test"
err = test2
concreteErr9 := &MyErrorImplementedByPointerReceiver{}
concreteErr9.s = "test"
err2 = concreteErr9
assert.False(t, errors.Is(err, err2))
}
2023-05-04
- 큰 단위의 함수를 테스트하는것은 비용이 많이 든다. 의존하는 컴포넌트들도 많고 각각의 컴포넌트들의 실행결과를 원하는 결과로 대체할수 있어야하기 때문이다. 게다가 실제 코드에서 모킹을 도저히 하기 어려운 점도 분명 존재한다. 가장 비용대비 효용이 큰 테스트는 도메인 코드들이기때문에, 이 코드들을 테스트하기 쉬운 구조로 리팩토링을 하는것도 중요해 보인다. 함수를 묶어서 처리하는 식으로 말이다. 그리고 그 함수들을 테스트하는것이 좋지 않을까 싶다. 그리고 각각의 컴포넌트들을 조합해서 실행하는 코드들에 대해서는 각각의 컴포넌트들을 쉽게 모킹할수 있어야 하는데, 현재 구조에서는 각각의 컴포넌트가 구체타입으로 되어있기 때문에 가짜구현으로 대체하는것이 불가능하다.(적어도 상속이 없는 고랭에서는 그래보인다, 그것이 가능하다고 해도 안티패턴이다.)
2023-05-10
- 오늘 go-redis go 패키지를 사용해서, hset 오퍼레이션을 처리할때
"redis: can't marshal %T (implement encoding.BinaryMarshaler)"
에러가 발생했다. 이것의 원인은 hset함수의 value로 넘기는 타입으로 몇가지 타입만(map[string]string
,map[string]interface{}
,[]string
,[]interface
)을 지원하기 때문에 발생하였다. value의 타입을 any가 아니고 몇가지 타입중 하나로 제한하면 더 좋을것같다. - Jira에서 predefined task 설정하려면, 티켓에 Move버튼을 클릭!
2023-05-19
- golang에서
sync.WaitGroup
사용시 주의할점:waitGroup
의Add
와Done
함수는 하나의 Goroutine에서 호출되는것이 원칙이다. - 항상 golang 채널에 대한 소유권은 송신측에서 한다. 수신측에서 채널을 임의로 close하면 송신측에서 panic이 발생할수 있다.
2023-06-03
git diff-index --quiet HEAD
: HEAD에 명시된 파일들과 현재 디렉토리에 있는 파일들을 비교하되 출력은 하지 않는다.git diff-index --quiet HEAD || git commit -m "Add changes" && git push
: 현재 stage된 파일들이 없다면git diff-index --quiet HEAD
이 부분은 true가 되며, 만약 stage된 파일이 존재하면 나머지 부분이 실행된다.
2023-06-16
- client에서 128bit uuid(v4)를 저장할때 string으로 받아서 저장하면 36byte 공간을 차지하므로 이 공간을 줄일필요가 있었다.
- 이를 줄이기 위한 방안을 팀에서 모색하는데 다음과 같은 아이디어가 있었다.
- 서버에서 클라이언트로 uuid를 int64 두개로 나눠서 전달
- 클라이언트에서 uuid를 byte시퀀스로 저장
- 64bit 공간을 차지하는 Snowflake ID를 사용
- 이를 줄이기 위한 방안을 팀에서 모색하는데 다음과 같은 아이디어가 있었다.
2023-06-23
- shell command에서 오류가 발생할 경우 로그 찍고 넘어가게 하려면 아래처럼 하면됨
1 2 3
test: open ./a.txt || : echo 'test'
2023-07-05
- defer 호출시 주의해야할 상황 ``` go func test() { var a = 1 defer fmt.Println(a) a = 2 }
func test1() { var a = 1 defer func() { fmt.Println(a) }() a = 2 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
위의 예시에서 test함수의 결과로는 1이 출력되고, test1함수의 결과로는 2가 출력된다. 이유는 test함수의 경우 a값이 1인 상태로 `fmt.Println(a)`가 콜스택에 쌓기게되고 test1함수의 경우 `func()` 가 콜스택에 쌓이게 되면서, func가 실행되는 시점에 a의 값을 불러오게 된다.
## 2023-07-12
- float 비교시 주의할점
floating-point 표현때문에 float 수를 비교할때 `=`를 사용하는것은 좋지 않은 방법일 수 있다.
따라서 absolute difference가 small threshold내에 있는지 확인하는것이 좋다.
``` go
package main
import (
"fmt"
"math"
)
const epsilon = 1e-9 // You can adjust the precision based on your needs
func AlmostEqual(a, b float64) bool {
return math.Abs(a-b) <= epsilon
}
func main() {
num1 := 0.1 + 0.2
num2 := 0.3
if AlmostEqual(num1, num2) {
fmt.Println("num1 and num2 are approximately equal")
} else {
fmt.Println("num1 and num2 are not equal")
}
}
2023-09-06
- var 키워드로 map을 선언할경우 nil이 됨을 유의해야함. 따라서 아래처럼 선언해야함.
1
mp := make(map[string]int)
2023-09-28
- 구조체에 포인터 변수가 있으면서 항상 nil 체크를 해야한다. 그렇지 않으면 서버가 죽어버리고 응답성이 확 나빠진다. 경험을 통해서 느끼니 배우는 깊이가 다른듯하다.