왜 이런 키워드들이 필요할까?
프로그래밍에서 "누가 무엇에 접근할 수 있는가?"와 "언제 값이 바뀔 수 있는가?"는 매우 중요한 문제입니다.
잘못된 접근이나 수정을 막아야 하는 이유:
• 버그 방지: 예상치 못한 곳에서 값이 바뀌면 추적이 어려움
• 성능 최적화: 불변값은 컴파일러가 최적화 가능
• 팀 협업: 다른 개발자가 건드리면 안 되는 코드 보호
• 메모리 관리: 불필요한 인스턴스 생성 방지
이런 문제들을 해결하기 위해
잘못된 접근이나 수정을 막아야 하는 이유:
• 버그 방지: 예상치 못한 곳에서 값이 바뀌면 추적이 어려움
• 성능 최적화: 불변값은 컴파일러가 최적화 가능
• 팀 협업: 다른 개발자가 건드리면 안 되는 코드 보호
• 메모리 관리: 불필요한 인스턴스 생성 방지
이런 문제들을 해결하기 위해
static
, final
, const
, private
등의 키워드가 존재합니다.static - "클래스의 것" vs "인스턴스의 것"
static
은 "이것은 클래스에 속한다"는 의미입니다. 개별 인스턴스가 아닌 클래스 자체에 속합니다.1. static 필드 - 클래스 공유 데이터
Dart
class Counter {
// ❌ 인스턴스 필드 - 각 객체마다 별도 값
int instanceCount = 0;
// ✅ static 필드 - 모든 객체가 공유하는 값
static int totalCount = 0;
Counter() {
instanceCount++; // 이 객체의 카운트만 증가
totalCount++; // 전체 카운트 증가 (모든 객체 공유)
}
void showCounts() {
print('이 객체: $instanceCount, 전체: $totalCount');
}
}
void main() {
final counter1 = Counter(); // totalCount = 1
final counter2 = Counter(); // totalCount = 2
final counter3 = Counter(); // totalCount = 3
counter1.showCounts(); // 이 객체: 1, 전체: 3
counter2.showCounts(); // 이 객체: 1, 전체: 3
counter3.showCounts(); // 이 객체: 1, 전체: 3
// static 필드는 클래스명으로 직접 접근
print('총 생성된 객체 수: ${Counter.totalCount}'); // 3
}
2. static 메소드 - 유틸리티 함수
Dart
class MathUtils {
// static 메소드는 인스턴스 없이 호출 가능
static double calculateCircleArea(double radius) {
return 3.14159 * radius * radius;
}
static String formatCurrency(double amount) {
return '₩${amount.toStringAsFixed(0).replaceAllMapped(
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
(Match m) => '${m[1]},',
)}';
}
static bool isEven(int number) {
return number % 2 == 0;
}
}
void main() {
// 인스턴스를 만들 필요 없이 바로 사용
print(MathUtils.calculateCircleArea(5.0)); // 78.53975
print(MathUtils.formatCurrency(1234567)); // ₩1,234,567
print(MathUtils.isEven(42)); // true
// 이렇게 할 필요 없음:
// final utils = MathUtils(); // 불필요!
// utils.calculateCircleArea(5.0);
}
3. static 변수의 실무 활용
Dart
class ApiConfig {
// 앱 전체에서 공유하는 설정값들
static String baseUrl = 'https://api.example.com';
static Duration timeout = Duration(seconds: 30);
static Map<String, String> defaultHeaders = {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
// 디버그 모드 확인
static bool isDebugMode = false;
// API 키 관리
static String? _apiKey;
static void setApiKey(String key) {
_apiKey = key;
}
static String get apiKey {
if (_apiKey == null) {
throw StateError('API 키가 설정되지 않았습니다');
}
return _apiKey!;
}
}
class Logger {
static void log(String message) {
if (ApiConfig.isDebugMode) {
print('[${DateTime.now()}] $message');
}
}
}
void main() {
// 전역 설정
ApiConfig.isDebugMode = true;
ApiConfig.setApiKey('secret-key-123');
// 어디서든 같은 설정 사용
Logger.log('앱 시작'); // [2023-12-01 10:30:45.123] 앱 시작
print('API URL: ${ApiConfig.baseUrl}');
print('API Key: ${ApiConfig.apiKey}');
}
final - "한 번만 설정 가능"
final
은 "값을 한 번만 설정할 수 있다"는 의미입니다. 하지만 런타임에 결정될 수 있습니다.1. final 필드 - 생성 후 불변
Dart
class User {
final String id; // 생성자에서 설정, 이후 변경 불가
final String name;
final DateTime createdAt;
String email; // 변경 가능한 필드
User({
required this.id,
required this.name,
required this.email,
}) : createdAt = DateTime.now(); // 런타임에 결정되는 final 값
void updateEmail(String newEmail) {
email = newEmail; // ✅ 가능
// name = '새이름'; // ❌ 컴파일 에러! final 필드는 변경 불가
}
}
void main() {
final user = User(
id: 'user123',
name: '김철수',
email: 'kim@example.com',
);
print('ID: ${user.id}'); // user123
print('생성시간: ${user.createdAt}'); // 2023-12-01 10:30:45.123
user.updateEmail('new@example.com'); // ✅ email은 변경 가능
// user.id = 'newid'; // ❌ final 필드는 변경 불가
}
2. final vs var - 언제 무엇을 쓸까?
Dart
void demonstrateFinalVsVar() {
// ✅ 변경되지 않을 값은 final 사용 (권장)
final String apiUrl = 'https://api.example.com';
final DateTime now = DateTime.now();
final List<String> fruits = ['사과', '바나나', '포도'];
// ✅ 변경될 수 있는 값은 var 사용
var counter = 0;
var isLoading = false;
// final 컬렉션의 내용은 변경 가능!
fruits.add('딸기'); // ✅ 가능 - 리스트 내용 변경
// fruits = ['새로운', '리스트']; // ❌ 불가능 - 리스트 자체 교체
// 변경 가능한 변수들
counter++; // ✅ 가능
isLoading = true; // ✅ 가능
// final 값들은 변경 불가
// apiUrl = '새로운 URL'; // ❌ 컴파일 에러
// now = DateTime.now(); // ❌ 컴파일 에러
print('과일: $fruits'); // [사과, 바나나, 포도, 딸기]
print('카운터: $counter'); // 1
}
3. late final - 나중에 초기화
Dart
class DatabaseService {
// late final - 나중에 한 번만 초기화
late final String connectionString;
late final DatabaseConnection _connection;
// 초기화 메소드
Future<void> initialize(String host, int port, String database) async {
// 한 번만 설정 가능
connectionString = 'postgresql://$host:$port/$database';
// 복잡한 초기화 과정
_connection = await DatabaseConnection.connect(connectionString);
print('데이터베이스 연결 완료: $connectionString');
}
Future<List<Map<String, dynamic>>> query(String sql) async {
// _connection이 초기화되지 않았으면 에러 발생
return await _connection.query(sql);
}
}
void main() async {
final db = DatabaseService();
// 초기화 전에는 접근 불가
// print(db.connectionString); // ❌ 런타임 에러!
await db.initialize('localhost', 5432, 'myapp');
// 이제 접근 가능
print(db.connectionString); // postgresql://localhost:5432/myapp
// 재초기화 시도하면 에러
// db.connectionString = 'new connection'; // ❌ 컴파일 에러!
}
const - "컴파일 타임 상수"
const
는 "컴파일 시점에 값이 결정되는 상수"입니다. final
보다 더 엄격하며, 성능상 이점이 있습니다.1. const vs final 차이점
Dart
class ConstantExamples {
// ✅ const - 컴파일 타임에 값이 결정됨
static const String appName = 'My Flutter App';
static const int maxRetries = 3;
static const Duration shortDelay = Duration(milliseconds: 500);
// ✅ final - 런타임에 값이 결정됨
static final String sessionId = DateTime.now().millisecondsSinceEpoch.toString();
static final DateTime appStartTime = DateTime.now();
// ❌ const는 런타임 값 불가
// static const DateTime now = DateTime.now(); // 컴파일 에러!
}
void main() {
// const 값들은 컴파일 시점에 이미 결정됨
print('앱 이름: ${ConstantExamples.appName}'); // My Flutter App
print('최대 재시도: ${ConstantExamples.maxRetries}'); // 3
// final 값들은 런타임에 결정됨
print('세션 ID: ${ConstantExamples.sessionId}'); // 1701234567890
print('앱 시작: ${ConstantExamples.appStartTime}'); // 2023-12-01 10:30:45.123
}
2. const 생성자 - 메모리 최적화
Dart
class Color {
final int red;
final int green;
final int blue;
// const 생성자 - 컴파일 타임에 인스턴스 생성
const Color(this.red, this.green, this.blue);
// 미리 정의된 색상들
static const Color black = Color(0, 0, 0);
static const Color white = Color(255, 255, 255);
static const Color red = Color(255, 0, 0);
static const Color green = Color(0, 255, 0);
static const Color blue = Color(0, 0, 255);
@override
String toString() => 'Color($red, $green, $blue)';
}
void main() {
// const 인스턴스들은 메모리에서 재사용됨
const color1 = Color(255, 0, 0);
const color2 = Color(255, 0, 0);
const color3 = Color.red;
// 모두 같은 메모리 주소를 가짐 (메모리 효율적!)
print(identical(color1, color2)); // true
print(identical(color1, color3)); // true
print(identical(color2, color3)); // true
// 런타임 생성은 매번 새 인스턴스
final color4 = Color(255, 0, 0);
final color5 = Color(255, 0, 0);
print(identical(color4, color5)); // false (다른 메모리 주소)
}
3. const 컬렉션
Dart
class AppConstants {
// ✅ const 리스트 - 불변 컬렉션
static const List<String> supportedLanguages = [
'ko',
'en',
'ja',
'zh',
];
// ✅ const 맵 - 불변 맵
static const Map<String, String> apiEndpoints = {
'users': '/api/v1/users',
'orders': '/api/v1/orders',
'products': '/api/v1/products',
};
// ✅ const Set - 불변 집합
static const Set<String> adminRoles = {
'super_admin',
'admin',
'moderator',
};
}
void main() {
// const 컬렉션은 완전히 불변
print(AppConstants.supportedLanguages); // [ko, en, ja, zh]
// 내용 변경 시도하면 런타임 에러
try {
AppConstants.supportedLanguages.add('fr'); // ❌ 런타임 에러!
} catch (e) {
print('에러: $e'); // Unsupported operation: Cannot add to unmodifiable list
}
// 읽기 전용 접근은 가능
final hasKorean = AppConstants.supportedLanguages.contains('ko');
print('한국어 지원: $hasKorean'); // true
}
private (_) - "외부 접근 금지"
Dart에서는
_
(언더스코어)로 시작하는 이름을 private으로 처리합니다. 같은 파일(라이브러리) 내에서만 접근 가능합니다.1. private 필드와 메소드
Dart
// user_service.dart 파일
class UserService {
// ✅ public 필드 - 외부에서 접근 가능
String version = '1.0.0';
// ❌ private 필드 - 외부에서 접근 불가
String _apiKey = 'secret-key-123';
int _retryCount = 0;
// ✅ public 메소드
Future<User> getUser(String id) async {
_logRequest('Fetching user: $id'); // private 메소드 호출
try {
return await _makeApiCall('/users/$id');
} catch (e) {
return await _handleError(e);
}
}
// ❌ private 메소드들 - 내부 구현 세부사항
void _logRequest(String message) {
print('[${DateTime.now()}] $message');
}
Future<User> _makeApiCall(String endpoint) async {
_retryCount++;
// API 호출 로직 (복잡한 내부 구현)
await Future.delayed(Duration(milliseconds: 500));
return User(id: '123', name: '김철수');
}
Future<User> _handleError(dynamic error) async {
_logRequest('Error occurred: $error');
// 에러 처리 로직
if (_retryCount < 3) {
return await _makeApiCall('/users/default');
}
throw Exception('사용자 조회 실패');
}
// ✅ public getter로 private 값에 안전하게 접근
int get currentRetryCount => _retryCount;
// ✅ private 값 변경을 위한 public 메소드
void resetRetryCount() {
_retryCount = 0;
}
}
class User {
final String id;
final String name;
User({required this.id, required this.name});
}
2. private 사용 예제
Dart
// main.dart 파일
import 'user_service.dart';
void main() {
final userService = UserService();
// ✅ public 멤버 접근 가능
print('버전: ${userService.version}');
print('재시도 횟수: ${userService.currentRetryCount}');
// ❌ private 멤버 접근 불가 (컴파일 에러)
// print(userService._apiKey); // 에러!
// userService._logRequest('test'); // 에러!
// userService._retryCount = 5; // 에러!
// ✅ public 메소드 호출 가능
userService.getUser('123');
userService.resetRetryCount();
}
3. private 생성자 - 싱글톤 패턴
Dart
class AppLogger {
static AppLogger? _instance;
// ❌ private 생성자 - 외부에서 직접 생성 불가
AppLogger._internal();
// ✅ public factory 생성자 - 싱글톤 보장
factory AppLogger() {
_instance ??= AppLogger._internal();
return _instance!;
}
void log(String message) {
print('[LOG] $message');
}
}
class DatabaseConnection {
String _connectionString;
bool _isConnected = false;
// ❌ private 생성자
DatabaseConnection._(this._connectionString);
// ✅ public factory 생성자 - 검증 로직 포함
factory DatabaseConnection.create(String host, int port, String database) {
if (host.isEmpty) {
throw ArgumentError('호스트는 비어있을 수 없습니다');
}
if (port <= 0 || port > 65535) {
throw ArgumentError('포트는 1-65535 범위여야 합니다');
}
final connectionString = 'postgresql://$host:$port/$database';
return DatabaseConnection._(connectionString);
}
Future<void> connect() async {
if (_isConnected) return;
// 연결 로직
await Future.delayed(Duration(seconds: 1));
_isConnected = true;
print('데이터베이스 연결됨: $_connectionString');
}
bool get isConnected => _isConnected;
}
void main() {
// ✅ 올바른 사용법
final logger1 = AppLogger();
final logger2 = AppLogger();
print(identical(logger1, logger2)); // true - 같은 인스턴스
final db = DatabaseConnection.create('localhost', 5432, 'myapp');
db.connect();
// ❌ private 생성자 직접 호출 불가
// final logger3 = AppLogger._internal(); // 컴파일 에러!
// final db2 = DatabaseConnection._('test'); // 컴파일 에러!
}
실무에서의 활용 패턴
1. Repository 패턴에서의 활용
Dart
abstract class UserRepository {
Future<User> getUser(String id);
Future<void> saveUser(User user);
}
class ApiUserRepository implements UserRepository {
// ✅ private - 외부에서 직접 접근하면 안 되는 구현 세부사항
final String _baseUrl;
final Map<String, String> _headers;
static const Duration _defaultTimeout = Duration(seconds: 30);
// ✅ final - 생성 후 변경되면 안 되는 설정
final Duration timeout;
ApiUserRepository({
required String baseUrl,
required Map<String, String> headers,
this.timeout = _defaultTimeout,
}) : _baseUrl = baseUrl,
_headers = Map.unmodifiable(headers); // 불변 맵 생성
@override
Future<User> getUser(String id) async {
final url = _buildUrl('/users/$id');
return await _makeRequest(url);
}
@override
Future<void> saveUser(User user) async {
final url = _buildUrl('/users/${user.id}');
await _makeRequest(url, method: 'PUT', body: user.toJson());
}
// ❌ private - 내부 구현 로직
String _buildUrl(String endpoint) {
return '$_baseUrl$endpoint';
}
Future<User> _makeRequest(String url, {String method = 'GET', Map<String, dynamic>? body}) async {
// HTTP 요청 로직
await Future.delayed(Duration(milliseconds: 500));
return User(id: '123', name: '김철수');
}
}
2. State Management에서의 활용
Dart
class CounterState {
// ✅ private - 외부에서 직접 수정하면 안 되는 상태
int _count = 0;
// ✅ public getter - 상태 읽기만 허용
int get count => _count;
// ✅ public 메소드 - 상태 변경을 위한 안전한 인터페이스
void increment() {
_count++;
_notifyListeners();
}
void decrement() {
if (_count > 0) { // 비즈니스 로직으로 보호
_count--;
_notifyListeners();
}
}
void reset() {
_count = 0;
_notifyListeners();
}
// ❌ private - 내부 구현 세부사항
final List<VoidCallback> _listeners = [];
void _notifyListeners() {
for (final listener in _listeners) {
listener();
}
}
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
}
typedef VoidCallback = void Function();
void main() {
final counterState = CounterState();
// ✅ 올바른 사용법
print('현재 카운트: ${counterState.count}'); // 0
counterState.increment();
counterState.increment();
print('카운트: ${counterState.count}'); // 2
counterState.decrement();
print('카운트: ${counterState.count}'); // 1
// ❌ 잘못된 사용법 (컴파일 에러)
// counterState._count = 100; // private 필드 직접 수정 불가
// counterState._notifyListeners(); // private 메소드 호출 불가
}
3. 설정 클래스 패턴
Dart
class AppConfig {
// ✅ static const - 앱 전체에서 사용하는 불변 설정
static const String appName = 'My Flutter App';
static const String version = '1.0.0';
static const Duration defaultTimeout = Duration(seconds: 30);
// ✅ static final - 런타임에 결정되는 전역 설정
static final String buildTime = DateTime.now().toIso8601String();
// ❌ private static - 내부에서만 사용하는 설정
static const String _encryptionKey = 'super-secret-key';
static const List<String> _allowedDomains = [
'api.myapp.com',
'staging-api.myapp.com',
];
// ✅ public 메소드 - 안전한 접근 인터페이스
static bool isAllowedDomain(String domain) {
return _allowedDomains.contains(domain);
}
static String encryptData(String data) {
// _encryptionKey를 사용한 암호화 로직
return 'encrypted_$data';
}
}
class EnvironmentConfig {
final String apiBaseUrl;
final bool isDebugMode;
final Duration cacheDuration;
// ✅ const 생성자 - 컴파일 타임 상수 환경
const EnvironmentConfig._({
required this.apiBaseUrl,
required this.isDebugMode,
required this.cacheDuration,
});
// ✅ 미리 정의된 환경들
static const EnvironmentConfig development = EnvironmentConfig._(
apiBaseUrl: 'https://dev-api.myapp.com',
isDebugMode: true,
cacheDuration: Duration(minutes: 1),
);
static const EnvironmentConfig production = EnvironmentConfig._(
apiBaseUrl: 'https://api.myapp.com',
isDebugMode: false,
cacheDuration: Duration(hours: 1),
);
// ✅ 현재 환경 (런타임에 결정)
static late final EnvironmentConfig current;
static void initialize(EnvironmentConfig config) {
current = config;
}
}
void main() {
// 환경 초기화
EnvironmentConfig.initialize(EnvironmentConfig.development);
// 설정 사용
print('앱 이름: ${AppConfig.appName}');
print('API URL: ${EnvironmentConfig.current.apiBaseUrl}');
print('디버그 모드: ${EnvironmentConfig.current.isDebugMode}');
print('도메인 허용: ${AppConfig.isAllowedDomain('api.myapp.com')}');
// ❌ private 설정에 직접 접근 불가
// print(AppConfig._encryptionKey); // 컴파일 에러!
}
헷갈리기 쉬운 개념들 정리
1. static vs instance
Dart
class ComparisonExample {
// 인스턴스 필드 - 각 객체마다 별도 메모리
String instanceField = 'instance';
// static 필드 - 모든 객체가 공유하는 하나의 메모리
static String staticField = 'static';
// 인스턴스 메소드 - 객체를 통해서만 호출 가능
void instanceMethod() {
print('Instance method called');
print('Instance field: $instanceField');
print('Static field: $staticField'); // static 접근 가능
}
// static 메소드 - 클래스명으로 직접 호출 가능
static void staticMethod() {
print('Static method called');
print('Static field: $staticField');
// print('Instance field: $instanceField'); // ❌ 에러! 인스턴스 필드 접근 불가
}
}
void main() {
// static 멤버는 인스턴스 없이도 접근 가능
print(ComparisonExample.staticField); // static
ComparisonExample.staticMethod(); // Static method called
// 인스턴스 멤버는 객체를 통해서만 접근
final obj = ComparisonExample();
print(obj.instanceField); // instance
obj.instanceMethod(); // Instance method called
}
2. final vs const vs static const
Dart
class ConstantComparison {
// final - 런타임에 한 번만 설정, 인스턴스마다 다를 수 있음
final String id = DateTime.now().millisecondsSinceEpoch.toString();
// static final - 런타임에 한 번만 설정, 모든 인스턴스가 공유
static final String sessionId = DateTime.now().millisecondsSinceEpoch.toString();
// const - 컴파일 타임 상수, 변경 불가
static const String appName = 'My App';
// static const - 컴파일 타임 상수, 모든 인스턴스가 공유
static const int maxUsers = 1000;
}
void main() {
final obj1 = ConstantComparison();
final obj2 = ConstantComparison();
// final - 인스턴스마다 다른 값
print('obj1 ID: ${obj1.id}'); // 1701234567890
print('obj2 ID: ${obj2.id}'); // 1701234567891
// static final - 모든 인스턴스가 같은 값 공유
print('Session ID: ${ConstantComparison.sessionId}'); // 모두 같은 값
// static const - 컴파일 타임에 결정된 상수
print('App Name: ${ConstantComparison.appName}'); // My App
print('Max Users: ${ConstantComparison.maxUsers}'); // 1000
}
3. private 범위 이해하기
Dart
// file1.dart
class ClassInFile1 {
String _privateField = 'private in file1';
void accessOtherPrivate(ClassInFile1 other) {
// 같은 파일의 같은 클래스라면 private 멤버 접근 가능
print(other._privateField); // ✅ 가능
}
}
class AnotherClassInFile1 {
void accessPrivate() {
final obj = ClassInFile1();
// 같은 파일의 다른 클래스에서도 private 멤버 접근 가능
print(obj._privateField); // ✅ 가능
}
}
// file2.dart (다른 파일)
// import 'file1.dart';
//
// class ClassInFile2 {
// void tryAccess() {
// final obj = ClassInFile1();
// print(obj._privateField); // ❌ 에러! 다른 파일에서는 접근 불가
// }
// }
실무 가이드라인
1. 언제 무엇을 사용할까?
static 사용 시기:
• 유틸리티 함수들 (
• 전역 설정값들 (
• 싱글톤 인스턴스 관리
• 캐시나 공유 상태
final 사용 시기:
• 생성 후 변경되면 안 되는 필드
• 생성자에서 설정되는 ID, 타임스탬프 등
• 의존성 주입으로 받은 객체들
const 사용 시기:
• 컴파일 타임에 알 수 있는 상수들
• 색상, 크기, 문자열 등 고정값들
• Widget에서 성능 최적화가 필요할 때
private 사용 시기:
• 외부에서 접근하면 안 되는 내부 구현
• 클래스의 내부 상태
• 헬퍼 메소드들
• 유틸리티 함수들 (
MathUtils.calculate()
)• 전역 설정값들 (
AppConfig.apiUrl
)• 싱글톤 인스턴스 관리
• 캐시나 공유 상태
final 사용 시기:
• 생성 후 변경되면 안 되는 필드
• 생성자에서 설정되는 ID, 타임스탬프 등
• 의존성 주입으로 받은 객체들
const 사용 시기:
• 컴파일 타임에 알 수 있는 상수들
• 색상, 크기, 문자열 등 고정값들
• Widget에서 성능 최적화가 필요할 때
private 사용 시기:
• 외부에서 접근하면 안 되는 내부 구현
• 클래스의 내부 상태
• 헬퍼 메소드들
2. 성능 최적화 팁
Dart
class PerformanceOptimized {
// ✅ static const - 메모리에 한 번만 생성
static const List<String> categories = ['전자제품', '의류', '도서'];
// ✅ late final - 필요할 때만 계산
late final String expensiveCalculation = _calculateSomethingExpensive();
// ✅ static final - 앱 전체에서 한 번만 생성
static final RegExp emailRegex = RegExp(r'^[^@]+@[^@]+\.[^@]+$');
String _calculateSomethingExpensive() {
// 복잡한 계산...
return '계산된 값';
}
// ✅ const 생성자 - Widget에서 성능 향상
const PerformanceOptimized.constant();
}
// Flutter Widget에서의 활용
class MyWidget extends StatelessWidget {
const MyWidget({Key? key}) : super(key: key); // const 생성자
@override
Widget build(BuildContext context) {
return const Text('Hello'); // const Widget - 리빌드 시 재생성 안됨
}
}
정리
Dart의 키워드들을 올바르게 사용하면 더 안전하고 효율적인 코드를 작성할 수 있습니다.
핵심 원칙:
• static: 클래스 레벨의 공유 데이터나 유틸리티 함수에 사용
• final: 한 번 설정 후 변경되면 안 되는 값에 사용
• const: 컴파일 타임 상수로 성능 최적화에 유리
• private (_): 외부에서 접근하면 안 되는 내부 구현에 사용
실무에서 기억할 점:
• 가능한 한 불변성을 선호하세요 (final, const 적극 활용)
• 외부 인터페이스와 내부 구현을 분리하세요 (private 활용)
• 성능이 중요한 곳에서는 const를 적극 활용하세요
• 전역 상태는 static으로 관리하되, 남용하지 마세요
이런 키워드들을 제대로 이해하고 사용하면 버그가 적고, 성능이 좋으며, 유지보수하기 쉬운 코드를 작성할 수 있습니다.
핵심 원칙:
• static: 클래스 레벨의 공유 데이터나 유틸리티 함수에 사용
• final: 한 번 설정 후 변경되면 안 되는 값에 사용
• const: 컴파일 타임 상수로 성능 최적화에 유리
• private (_): 외부에서 접근하면 안 되는 내부 구현에 사용
실무에서 기억할 점:
• 가능한 한 불변성을 선호하세요 (final, const 적극 활용)
• 외부 인터페이스와 내부 구현을 분리하세요 (private 활용)
• 성능이 중요한 곳에서는 const를 적극 활용하세요
• 전역 상태는 static으로 관리하되, 남용하지 마세요
이런 키워드들을 제대로 이해하고 사용하면 버그가 적고, 성능이 좋으며, 유지보수하기 쉬운 코드를 작성할 수 있습니다.