상수는 변하지 않는 값을 말한다. 변수는 대입문을 통해서 값을 수시로 바꿀 수 있지만 상수는 초기화된 값이 변하지 않는다. 정수, 실수, 문자열 등 기본 타입값들만 상수로 선언될 수 잇다.
Go 언어에서 상수로 사용될 수 있는 타입은 다음과 같다.
상수 선언 방식은 변수와 비슷하다. 변수를 뜻하는 var 대신 상수를 뜻하는 const 키워드를 사용한다는 점이 다르다.
const ConstValue int = 10
상수명 규칙은 변수명과 같다. 함수 외부에 선언되어 있고 첫 글자가 대문자인 상수는 패키지 외부로 공개되는 상수이다. 상수는 한 번 선언되면 그 값을 바꿀 수 없다. 상수는 값으로만 동작하기 때문에 대입문의 좌변에 올 수 없다.
상수로 변하지 않는 값에 이름을 부여하면 매번 값을 쓰지 않고 편리하게 이용할 수 있다.
파이값을 상수와 변수로 선언해서 사용하는 예제를 살펴보자.
package main
import "fmt"
func main() {
const PI1 float64 = 3.1415926535
var PI2 float64 = 3.1415926565
PI2 = 4
fmt.Println("원주율: %f\n", PI1)
fmt.Println("원주율: %f\n", PI2)
}
상수를 코드값으로 사용할 수 있다. 코드값이란 어떤 숫자에 의미를 부여하는 것을 말한다.
동물 코드값에 따라서 다른 문자열을 출력하는 예제를 살펴보자.
package main
import "fmt"
const Pig int = 0
const Cow int = 1
const Chicken int = 2
func PrintAnimal(animal int) {
if animal == Pig{
fmt.Println("꿀꿀")
} else if animal == Cow{
fmt.Println("음메")
} else if animal == Chicken{
fmt.Println("꼬끼오")
}else {
fmt.Println("...")
}
}
func main() {
PrintAnimal(Pig)
PrintAnimal(Cow)
PrintAnimal(Chicken)
}
상수 선언 시 타입을 명시하지 않을 수 있다. 그러면 타입 없는 상수가 된다. 타입 없는 상숫값은 타입이 정해지지 않은 상태로 사용된다. 다음 예제를 보겠다.
package main
import "fmt"
const PI = 3.14
const FloatPI float64 = 3.14
func main() {
var a int = PI * 100
var b int = FloatPI * 100
fmt.Println(a)
fmt.Println(b)
}
이 코드는 오류가 난다.
PI 값은 타입이 없기 때문에 3.14 숫자로만 동작한다. FloatPI는 float64 타입 상수이다. 값은 3.14이다. PI 100 은 3.14 100 으로 치환되어 계산된 결과 314이다. 314는 정수 타입 변수에 대입할 수 있다.
FloatPI는 float64 타입이기 때문에 타입 오류가 발생한다.
타입 없는 상수는 변수에 복사될 때 타입이 정해지기 때문에 여러 타입에 사용되는 상숫값을 사용할 때 편리하다.
if…else 예제를 살펴보자.
package main
import "fmt"
func main() {
light := "red"
if light == "green" {
fmt.Println("길을 건던다")
} else {
fmt.Println("대기한다.")
}
}
else문은 생략 가능한다. else if 구문까지 사용하는 온도 조절장치 예제를 살펴보자.
package main
import "fmt"
func main() {
temp := 33
if temp > 28 {
fmt.Println("에어컨을 켠다")
} else if temp <= 3 {
fmt.Println("히터를 켠다")
} else {
fmt.Println("대기한다")
}
}
if문 안에 if문을 중접해 사용할 수도 있다. 복잡한 경우를 표현할 때 사용한다. 예를 들어 아래 예문을 if문 하나로 표현하기가 조금 어렵다.
package main
import "fmt"
func HasRichFriend() bool {
return true
}
func GetFriendCount() int {
return 3
}
func main() {
price := 35000
if price > 50000 {
if HasRichFriend() {
fmt.Println("앗 신발끈이 풀렸네")
} else {
fmt.Println("나눠내자")
}
} else if price >= 30000 && price <= 50000 {
if GetFriendCount() > 3 {
fmt.Println("어이쿠 신발끈이..")
} else {
fmt.Println("사람도 얼마 없는데 나눠 내자")
}
} else {
fmt.Println("오늘은 내가 쏜다")
}
}
if문 조건을 검사하기 전에 초기문을 넣을 수 있다. 초기문은 검사에 사용할 변수를 초기화할 때 주로 사용한다. 형식은 다음과 같다.
if 초기문; 조건문 {
문장
}
초기문 자리에 하나의 구문이 올 수 있으며 끝에 ; 를 붙여서 구문이 끝남을 표시한다. 그리고 조건문을 넣는다.
if filename, success := UploadFile(); success {
fmt.Println("Upload success", filename)
} else {
fmt.Println("Failed to upload")
}
먼저 UploadFile() 함수를 실행하고 filename과 success 변수에 반환값을 저장한다. 그리고 그 함수 성공 여부에 따라 다른 메시지를 출력한다. 이렇게 if 초기문은 어떤 함수를 실행하고 그 함수의 결과를 검사할 때 주로 사용한다.
초기문에서 선언한 변수의 범위는 if문 안으로 한정된다는 사실에 주의해야 한다.
package main
import "fmt"
func getAge() (int, bool) {
return 33, true
}
func main() {
if age, ok := getAge(); ok && age < 20 {
fmt.Println("You are young", age)
} else if ok && age < 30 {
fmt.Println("Nice age", age)
} else if ok {
fmt.Println("You are beautiful", age)
} else {
fmt.Println("Error")
}
}
일반적으로 변수는 변수가 선언된 중괄호를 벗어나면 소멸되지만 if 초기문에 선언된 변수는 if문이 종료되기 전까지 유지된다.
package main
import "fmt"
func main() {
a := 3
switch a {
case 1:
fmt.Println("a==1")
case 2:
fmt.Println("a==2")
case 3:
fmt.Println("a==3")
case 4:
fmt.Println("a==4")
default:
fmt.Println("a>4")
}
}
a 값에 따라서 다른 메시지를 출력한다.
package main
import "fmt"
func main() {
day := 3
if day == 1{
fmt.Println("첫째 날입니다.")
fmt.Println("오늘은 팀미팅이 있습니다.")
}else if day == 2{
fmt.Println("둘째 날입니다.")
fmt.Println("새로운 팀원 면접이 있습니다.")
}else if day == 3{
fmt.Println("셋째 날입니다.")
fmt.Println("설계안을 확정하는 날입니다.")
}else if day == 4{
fmt.Println("넷째 날입니다.")
fmt.Println("예산을 확정하는 날입니다.")
}else if day == 5{
fmt.Println("다섯째 날입니다.")
fmt.Println("최종 계약하는 날입니다.")
}else {
fmt.Println("프로젝트를 진행하세요")
}
}
if-else문을 사용하여 날짜별로 다른 메시지를 출력하는 이 예제에 else-if가 너무 많아서 동작이 한 눈에 보이지 않는 문제가 있다.
switch문을 사용하면 다음과 같이 깔끔하게 변경할 수 있다.
package main
import "fmt"
func main() {
day := 3
switch day {
case 1:
fmt.Println("첫째 날입니다.")
fmt.Println("오늘은 팀미팅이 있습니다.")
case 2:
fmt.Println("들째 날입니다.")
fmt.Println("오늘은 면접이 있습니다.")
case 3:
fmt.Println("섯째 날입니다.")
fmt.Println("설계안을 확정하는 날입니다.")
case 4:
fmt.Println("넷째 날입니다.")
fmt.Println("예산을 확정하는 날입니다.")
case 5:
fmt.Println("다섯째 날입니다.")
fmt.Println("최종 계약하는 날입니다.")
default:
fmt.Println("프로젝트를 진행하세요")
}
}
위의 두 코드는 같인 동작을 하지만 if문을 사용한 예제보다 switch문을 사용한 예제가 가독성이 더 좋다. switch문을 이용하면 복잡한 if문을 깔끔하게 정리할 수 있다.
하나의 case는 하나 이상의 값을 비교할 수 있다. 각 값은 쉼표 , 로 구분한다.
package main
import "fmt"
func main() {
day := "thursday"
switch day {
case "monday", "tuesday":
fmt.Println("월, 화요일은 수업 가는 날입니다.")
case "wednesday", "thursday", "friday":
fmt.Println("수, 목, 금요일은 실습 가는 날입니다.")
}
}
하나의 case에 여러 값을 검사할 수 있다.
package main
import "fmt"
func main() {
temp := 18
switch true {
case temp < 10, temp >30:
fmt.Println("바깥 활동하기 좋은 날씨가 아니다.")
case temp >= 100 && temp < 20:
fmt.Println("약간 추울 수 있으니 가벼운 겉옷을 준비하세요.")
case temp >= 15 && temp < 25:
fmt.Println("야외 활동하기 좋은 날씨입니다.")
default:
fmt.Println("따뜻합니다.")
}
}
switch문은 비굣값과 case의 값이 같아지는 경우를 찾는 구문이기 때문에 비굣값을 true로 할 경우 case의 조건문이 true가 되는 경우가 실행된다.
if문과 마찬가지로 switch문에서 초기문을 넣을 수 있다. 형식은 다음과 같다.
switch 초기문; 비굣값{
case 값1:
...
case rkqt2:
...
default:
}
예제를 살펴보며 알아보겠다.
package main
import "fmt"
func getMyAge() int {
return 22
}
func main() {
switch age := getMyAge(); age { //getMyAge() 결괏값 비교
case 10:
fmt.Println("Teenager")
case 33:
fmt.Println("Pair 3")
default:
fmt.Println("My age is", age)
}
}
먼저 age := getMyAge() 가 실행되어 age가 초기화된다. 그리고 switch문의 비교값으로 age가 사용된다. age는 switch문에서 선언된 변수이기 때문에 switch문이 종료되기 전까지 접근할 수 있다. 하지만 switch문이 종료되면 age도 사라진다.
초기문으로 age 변수를 초기화하고 비굣값을 true로 하여서 조건문을 통해 age에 해당하는 세대를 표시하는 예제를 살펴보자.
package main
import "fmt"
func getMyAge() int{
return 22
}
func main() {
switch age := getMyAge(); true {
case age < 10:
fmt.Println("Child")
case age < 20:
fmt.Println("Teenager")
case age < 30:
fmt.Println("20s")
default:
fmt.Println("My age is", age)
}
}
const 열거값에 따라 수행되는 로직을 변경할 때 switch문을 주로 사용한다.
package main
import "fmt"
type ColorType int
const (
Red ColorType = iota
Blue
Green
Yellow
)
func colorToString(color ColorType) string {
switch color {
case Red:
return "Red"
case Blue:
return "Blue"
case Green:
return "Green"
case Yellow:
return "Yellow"
default:
return "Undefined"
}
}
func getMyFavoriteColor() ColorType {
return Blue
}
func main() {
fmt.Println("My favorite color is", colorToString(getMyFavoriteColor()))
}
package main
import "fmt"
func main() {
a := 3
switch a {
case 1:
fmt.Println("a == 1")
break
case 2:
fmt.Println("a == 2")
break
case 3:
fmt.Println("a == 3")
case 4:
fmt.Println("a == 4")
default:
fmt.Println("a > 4")
}
}
case 1과 case 2의 경우 break문을 사용해서 switch문을 빠져나갔다. case 3과 case 4는 사용하지 않았다. Go 언어에서는 break를 사용하든 사용하지 않든 상관없이 case 하나를 실행 후 switch문을 빠져나간다.
그런데 만약 하나의 case문 실행 후 다음 case문까지 같이 실행하고 싶을 땐 fallthrough 키워드를 사용한다. case 마지막에 fallthrough 키워드를 사용하면 다음 case 문까지 같이 실행된다.
fallthrough 키워드 동작을 확인해보겠다.
package main
import "fmt"
func main() {
a := 3
switch a {
case 1:
fmt.Println("a == 1")
break
case 2:
fmt.Println("a == 2")
case 3:
fmt.Println("a == 3")
fallthrough
case 4:
fmt.Println("a == 5")
default:
fmt.Println("a > 5")
}
}
case 3 구문 마지막에 fallthrough 키워드를 사용했다. 그 결과 case 4까지 실행됐다.