여러분들은 프로그래밍할 때 스코프를 의식하고 하시나요? 프로그래밍에서 스코프는 유지보수성에 큰 영향을 줍니다.
1. Scope(스코프)란?
자바에서 스코프(scope)란 변수나 메서드의 유효 범위를 말한다. 즉, 변수나 메서드가 어디에서 접근할 수 있는지를 결정한다. 스코프는 중괄호 {}로 둘러싸인 코드 블록에 의해 결정된다. 어떠한 범위 밖에 둔 변수 등은 일반적으로 그 이름만으로는 참조할 수 없다. 이 때 이런 변수는 '스코프 밖에 있다.', '안 보인다.'라고 말한다.
'보인다'라는 것은 그것들이 프로그램에서 사용될 수 있다는 뜻이다. 변수라면 변수명을 지정해 값을 읽고 쓸 수 있고, 메소드라면 호출해 사용할 수 있다. '사용할 수 있다'라는 것은 그것들에 '의존한다'는 뜻이다. 코드를 한 곳만 고쳐 쓰고 싶은데 수십 군데를 고쳐야 하는 경험을 해봤다면 스코프의 중요성을 알게 된다. 또한 '의존도가 크다'라는 것은 디버깅 할 때 조사하지 않으면 안 될 정도로 범위가 넓다라는 의미이다. 변수나 메소드가 사용되고 안되고와는 상관 없이 스코프의 범위가 결국 조사 대상이 되므로 스코프는 의존성에 큰 영향을 미친다.

변수가 메소드의 스코프 = 보이는 범위 = 사용할 수 있는 범위 = 의존하는 범위 = 의존성에 영향을 주는 범위
변수나 메소드의 스코프를 작게 하면 '보이는 범위'가 작아져서 '사용할 수 있는 범위'도 작아지고, '의존하는 범위'도 작아지게 된다. 스코프를 작게 함으로써 의존성이 적고 변경이 쉬운 프로그램을 작성할 수 있게 된다.
Scope(스코프)에서 중요한 것 = 스코프를 작게 하는 것
왜? 스코프를 작게 하면 의존도가 줄어들어 유지보수가 편한 것은 물론이고, 프로그래밍 자체의 복잡도도 줄일 수 있다. 즉, 문제 영역이 작고 이해 가능한 영역으로 줄어들기 때문에 개발자가 기억해야 되는 것, 주의할 것들에 대한 범위도 줄어든다.
2. 변수의 Scope(스코프)
2-1. 로컬 변수의 스코프
로컬 변수는 메소드 등의 내부에서 선언된 일시적인 변수예요. Java나 Ruby에서 로컬변수는 선언된 장소로부터 스코프가 시작 되어, 선언된 블록이 끝나면 스코프가 종료돼요. => 블록 스코프

[예] 스코프가 너무 긴 로컬 변수
public boolean isClosing(Date now) {
Date open = null; // 로컬변수 open/close의 선언을 너무 일찍함. 스코프 시작
Date close = null;
for(Timetable table : tables) {
open = getDate(table.openDateString()); // 실제로 로컬변수가 사용되는 스코프는
close = getDate(table.openCloseStrin()); // for문 안쪽 뿐임
if(contains(now, open, close)) {
return false;
}
}
return true; // 스코프가 끝
}
위 코드를 짧게 고쳐쓰면 아래 코드가 됩니다. 로컬 변수 open/close의 선언을 실제로 사용하기 직전에 바로 해줌으로써 스코프가 작아진 모습을 확인 할 수 있어요. 로컬변수의 스코프가 작아지면 로컬 변수를 참조할 수 있는 범위도 작아집니다. 즉 해당 로컬 변수의 영향 범위가 작아져서 open/close의 이름을 변경하거나 형태를 변경해도 그 변경의 영향이 미치는 범위가 국소적으로 끝납니다.
public boolean isClosing(Date now) {
for(Timetable table : tables) {
Date open = getDate(table.openDateString()); // 스코프 시작
Date close = getDate(table.openCloseStrin());
if(contains(now, open, close)) {
return false; // 스코프 끝
}
}
return true;
}
또 처리를 별도의 메소드로 추출하는 리팩토링을 실시하는 경우에도 로컬 변수 통째로 별도의 메소드로 이동할 수 있어요. 스코프를 작게 함으로써 작은 범위로 처리 내용이 좁혀져서 리팩토링이 쉽습니다.
스코프 줄이기
1. 변수는 사용하기 직전에 선언한다.
2. 메소드로 추출한다
3. 반복자(Iterator)에서 사용하는 일시 변수의 스코프를 루프 내로 한정한다.
4. 대입되지 않는 변수에는 final을 붙인다.
2-2. 필드 변수
필드 변수는 인스턴스(객체)별로 보관/유지되는 변수를 말해요. 필드 변수의 스코프는 필드 변수에 지정된 접근제한자에 의해 정해져요.(public/private 등) 필드 변수에 대한 가시성은 스코프가 작은 순서대로 다음 4가지로 정리 해 볼 수 있어요.
private < package private < protected < public
private 객체 내에서만 액세스 가능 |
package private 동일 패키지로부터만 액세스 가능 |
protected 동일 패키지와 하위 클래스의 객체로부터만 액세스 가능 |
public 다른 객체로부터 액세스 가능 |
필드 변수는 private이 기본값입니다. 필드 변수를 외부나 하위 클래스로부터 액세스 하고 싶은 경우는 set/get 등 액세스용 메소드를 사용해야 합니다.
// 필드 변수
private int count;
//set
public void setCount(int count) {
this.count = count;
}
// get
public int getCount() {
return this.count;
}
2-3. 클래스 변수
클래스 변수는 클래스가 보관/유지하는 변수예요. 클래스 변수의 스코프는 거의 대부분의 언어에서 필드변수와 동일한 가시성을 지정할 수 있어요. 자바에서 클래스 변수도 필드 변수와 같이 4종류가 있어요.
private < package private < protected < public
private 클래스 내에서만 액세스 가능 |
package private 동일 패키지로부터만 액세스 가능 |
protected 동일 패키지와 하위 클래스로부터만 액세스 가능 |
public 다른 클래스로부터 액세스 가능 |
클래스 변수도 private가 기본이예요. 필요에 따라 외부로부터 액세스 할 필요가 있으면 순서대로 package private, protected, public 키워드를 이용해서 가시성을 높여 나갈 수 있어요. 클래스 변수는 정수로도 이용할 수 있는데요. final 키워드를 붙이면 됩니다. final을 붙이면 외부로부터 값을 변경할 수가 없기 때문에 public으로 공개해도 비교적 안전한 편이예요.
public class DateUtils {
// 1년의 time 값을 나타내는 정수, 값 변경 불가
public static final long TIME_1YEAR = 60 * 60 * 24 * 365;
// 한 달의 time 값을 나타내는 정수, 값 변경 불가
public static final long TIME_1MONTH = 60 * 60 * 24 * 30;
}
대신 정수가 객체인 경우는 내부를 변경하는 것이 가능하기 때문에 조심해야 합니다. 다음 코드에서 컬러 리스트를 모든 코드에서 액세스 가능하기 때문에 내부값을 임의로 변경하기가 쉽습니다.
public class Colors {
public static final List<Stirng> BASIC_COLORS =
Arrays.asList(new String[] { "blue", "red", "green" });
}
public class Client {
public static void main(String[] args) {
// 누구라도 값을 변경할 수 있게 된다.
Colors.BASIC_COLORS.add("yellow");
}
}
위의 코드에서 아무나 값을 바꿀 수 없도록 하기 위해서는 컬렉션을 써 주면 됩니다.
public class Colors {
// Collections#unmodifiableList 메소드 사용
// 변경 불가능한 List 작성
public static final List<String> BASIC_COLORS =
Collections.unmodifableList(
Arrays.asList(new String[] {"blue", "red", "green"}));
}
public class Client {
public static void main(String[] args) {
// 값을 변경하려고 하면 UnsupportedOperationException 이 발생(안전)
Colors.BASIC_COLORS.add("yellow");
}
}
3. 메소드의 스코프
3-1. 인스턴스 메소드
인스턴스 메소드는 인스턴스(객체)에 결부되어 호출할 수 있는 메소드를 말해요. 인스턴스 메소드가 public인 경우, 메소드는 외부로 공개되어 있어 자유롭게 호출되어져 버리기 때문에 유지보수성을 높이기 위해서라면 꼭 필요한 것만 public으로 해야 합니다.
3-2. 클래스 메소드
클래스 메소드(static 메소드)는 클래스에 속해 있는 메소드입니다. 자바에서는 클래스 메소드를 static 키워드를 붙여서 선언해요. 클래스 메소드는 필드 변수를 액세스 할 수 없습니다. 필드 변수의 관점에서 보면, 인스턴스 메소드를 클래스 메소드로 변경하는 경우에는 영향 받는 메소드가 줄어들게 되서 결과적으로 스코프가 작아지게 됩니다. 필드 변수에 액세스 할 필요가 없고 오버라이드 등이 필요가 없는 메소드는 클래스 메소드로 하는 편이 좋은 경우가 많습니다.
인스턴트 메소드
private int count; // 변수 count의 스코프 시작
private String regexGroup(String regex) {
// 필드 변수에 액세스 가능
return "(" + regex + ")";
} // 변수 count의 스코프 끝
위 코드를 static 메소드로 변경
private int count; // 변수 count의 스코프 시작
.
.
.
// 변수 count의 스코프 끝
private static String regexGroup(String regex) {
// 필드 변수에 액세스 불가
return "(" + regex + ")";
}
.
.
.
// 변수 count의 스코프
4. 클래스의 스코프
4-1. 클래스의 스코프
자바에서 클래스의 스코프는 private, package private, protected, public 4종류이다. 클래스의 선언에서는 주로 public을 사용하는 경우가 많습니다. 그러나 프레임워크나 라이브러리를 작성하는 상황에서 package private를 사용하는 경우가 있습니다.
라이브러리 이용자에게 알리고 싶지 않은 클래스는 package private해 두면 나중에 클래스를 변경 할 때에도 그 변경의 영향을 이용자에게 주지 않게 할 수 있습니다. 즉 라이브러리 이용자가 알 필요 없는 클래스는 package private로 정리해 두면 됩니다. 그리고 private는 두 가지의 목적으로 사용되는데, 1. 인스턴스의 생성을 해당 클래스에서만 실시하게 하고 싶은 경우(디자인 패턴의 싱글톤 패턴 등)입니다. 하지만 메인 클래스는 private로 선언할 수 없기 때문에 생성자(constructor)을 private로 선언해서 다른 클래스 안에서는 인스턴스의 생성을 할 수 없게 해줍니다.

포스팅에 참고한 자료 : 좋은 코드를 작성하는 기술/아가타 토시타카, 정인식
'Java' 카테고리의 다른 글
(자바) 뮤직플레이어 프로그램 만들기(MVC패턴 적용 안한 상태) (1) | 2024.04.19 |
---|---|
[Java] this 키워드의 의미와 사용 방법 3가지(예시 코드 포함) (0) | 2024.04.19 |
[자바] 예시로 쉽게 이해하는 swtich-case문과 병합처리 (2) | 2024.03.29 |
[자바] 예시를 통해 쉽게 이해하는 이스케이프 시퀀스(Escape sequence)와 텍스트 블록(""") (3) | 2024.03.29 |
[java] 자바 배열 생성하는 4가지 방법과 배열 출력하기 (2) | 2024.03.21 |
여러분들은 프로그래밍할 때 스코프를 의식하고 하시나요? 프로그래밍에서 스코프는 유지보수성에 큰 영향을 줍니다.
1. Scope(스코프)란?
자바에서 스코프(scope)란 변수나 메서드의 유효 범위를 말한다. 즉, 변수나 메서드가 어디에서 접근할 수 있는지를 결정한다. 스코프는 중괄호 {}로 둘러싸인 코드 블록에 의해 결정된다. 어떠한 범위 밖에 둔 변수 등은 일반적으로 그 이름만으로는 참조할 수 없다. 이 때 이런 변수는 '스코프 밖에 있다.', '안 보인다.'라고 말한다.
'보인다'라는 것은 그것들이 프로그램에서 사용될 수 있다는 뜻이다. 변수라면 변수명을 지정해 값을 읽고 쓸 수 있고, 메소드라면 호출해 사용할 수 있다. '사용할 수 있다'라는 것은 그것들에 '의존한다'는 뜻이다. 코드를 한 곳만 고쳐 쓰고 싶은데 수십 군데를 고쳐야 하는 경험을 해봤다면 스코프의 중요성을 알게 된다. 또한 '의존도가 크다'라는 것은 디버깅 할 때 조사하지 않으면 안 될 정도로 범위가 넓다라는 의미이다. 변수나 메소드가 사용되고 안되고와는 상관 없이 스코프의 범위가 결국 조사 대상이 되므로 스코프는 의존성에 큰 영향을 미친다.

변수가 메소드의 스코프 = 보이는 범위 = 사용할 수 있는 범위 = 의존하는 범위 = 의존성에 영향을 주는 범위
변수나 메소드의 스코프를 작게 하면 '보이는 범위'가 작아져서 '사용할 수 있는 범위'도 작아지고, '의존하는 범위'도 작아지게 된다. 스코프를 작게 함으로써 의존성이 적고 변경이 쉬운 프로그램을 작성할 수 있게 된다.
Scope(스코프)에서 중요한 것 = 스코프를 작게 하는 것
왜? 스코프를 작게 하면 의존도가 줄어들어 유지보수가 편한 것은 물론이고, 프로그래밍 자체의 복잡도도 줄일 수 있다. 즉, 문제 영역이 작고 이해 가능한 영역으로 줄어들기 때문에 개발자가 기억해야 되는 것, 주의할 것들에 대한 범위도 줄어든다.
2. 변수의 Scope(스코프)
2-1. 로컬 변수의 스코프
로컬 변수는 메소드 등의 내부에서 선언된 일시적인 변수예요. Java나 Ruby에서 로컬변수는 선언된 장소로부터 스코프가 시작 되어, 선언된 블록이 끝나면 스코프가 종료돼요. => 블록 스코프

[예] 스코프가 너무 긴 로컬 변수
public boolean isClosing(Date now) { Date open = null; // 로컬변수 open/close의 선언을 너무 일찍함. 스코프 시작 Date close = null; for(Timetable table : tables) { open = getDate(table.openDateString()); // 실제로 로컬변수가 사용되는 스코프는 close = getDate(table.openCloseStrin()); // for문 안쪽 뿐임 if(contains(now, open, close)) { return false; } } return true; // 스코프가 끝 }
위 코드를 짧게 고쳐쓰면 아래 코드가 됩니다. 로컬 변수 open/close의 선언을 실제로 사용하기 직전에 바로 해줌으로써 스코프가 작아진 모습을 확인 할 수 있어요. 로컬변수의 스코프가 작아지면 로컬 변수를 참조할 수 있는 범위도 작아집니다. 즉 해당 로컬 변수의 영향 범위가 작아져서 open/close의 이름을 변경하거나 형태를 변경해도 그 변경의 영향이 미치는 범위가 국소적으로 끝납니다.
public boolean isClosing(Date now) { for(Timetable table : tables) { Date open = getDate(table.openDateString()); // 스코프 시작 Date close = getDate(table.openCloseStrin()); if(contains(now, open, close)) { return false; // 스코프 끝 } } return true; }
또 처리를 별도의 메소드로 추출하는 리팩토링을 실시하는 경우에도 로컬 변수 통째로 별도의 메소드로 이동할 수 있어요. 스코프를 작게 함으로써 작은 범위로 처리 내용이 좁혀져서 리팩토링이 쉽습니다.
스코프 줄이기
1. 변수는 사용하기 직전에 선언한다.
2. 메소드로 추출한다
3. 반복자(Iterator)에서 사용하는 일시 변수의 스코프를 루프 내로 한정한다.
4. 대입되지 않는 변수에는 final을 붙인다.
2-2. 필드 변수
필드 변수는 인스턴스(객체)별로 보관/유지되는 변수를 말해요. 필드 변수의 스코프는 필드 변수에 지정된 접근제한자에 의해 정해져요.(public/private 등) 필드 변수에 대한 가시성은 스코프가 작은 순서대로 다음 4가지로 정리 해 볼 수 있어요.
private < package private < protected < public
private 객체 내에서만 액세스 가능 |
package private 동일 패키지로부터만 액세스 가능 |
protected 동일 패키지와 하위 클래스의 객체로부터만 액세스 가능 |
public 다른 객체로부터 액세스 가능 |
필드 변수는 private이 기본값입니다. 필드 변수를 외부나 하위 클래스로부터 액세스 하고 싶은 경우는 set/get 등 액세스용 메소드를 사용해야 합니다.
// 필드 변수 private int count; //set public void setCount(int count) { this.count = count; } // get public int getCount() { return this.count; }
2-3. 클래스 변수
클래스 변수는 클래스가 보관/유지하는 변수예요. 클래스 변수의 스코프는 거의 대부분의 언어에서 필드변수와 동일한 가시성을 지정할 수 있어요. 자바에서 클래스 변수도 필드 변수와 같이 4종류가 있어요.
private < package private < protected < public
private 클래스 내에서만 액세스 가능 |
package private 동일 패키지로부터만 액세스 가능 |
protected 동일 패키지와 하위 클래스로부터만 액세스 가능 |
public 다른 클래스로부터 액세스 가능 |
클래스 변수도 private가 기본이예요. 필요에 따라 외부로부터 액세스 할 필요가 있으면 순서대로 package private, protected, public 키워드를 이용해서 가시성을 높여 나갈 수 있어요. 클래스 변수는 정수로도 이용할 수 있는데요. final 키워드를 붙이면 됩니다. final을 붙이면 외부로부터 값을 변경할 수가 없기 때문에 public으로 공개해도 비교적 안전한 편이예요.
public class DateUtils { // 1년의 time 값을 나타내는 정수, 값 변경 불가 public static final long TIME_1YEAR = 60 * 60 * 24 * 365; // 한 달의 time 값을 나타내는 정수, 값 변경 불가 public static final long TIME_1MONTH = 60 * 60 * 24 * 30; }
대신 정수가 객체인 경우는 내부를 변경하는 것이 가능하기 때문에 조심해야 합니다. 다음 코드에서 컬러 리스트를 모든 코드에서 액세스 가능하기 때문에 내부값을 임의로 변경하기가 쉽습니다.
public class Colors { public static final List<Stirng> BASIC_COLORS = Arrays.asList(new String[] { "blue", "red", "green" }); } public class Client { public static void main(String[] args) { // 누구라도 값을 변경할 수 있게 된다. Colors.BASIC_COLORS.add("yellow"); } }
위의 코드에서 아무나 값을 바꿀 수 없도록 하기 위해서는 컬렉션을 써 주면 됩니다.
public class Colors { // Collections#unmodifiableList 메소드 사용 // 변경 불가능한 List 작성 public static final List<String> BASIC_COLORS = Collections.unmodifableList( Arrays.asList(new String[] {"blue", "red", "green"})); } public class Client { public static void main(String[] args) { // 값을 변경하려고 하면 UnsupportedOperationException 이 발생(안전) Colors.BASIC_COLORS.add("yellow"); } }
3. 메소드의 스코프
3-1. 인스턴스 메소드
인스턴스 메소드는 인스턴스(객체)에 결부되어 호출할 수 있는 메소드를 말해요. 인스턴스 메소드가 public인 경우, 메소드는 외부로 공개되어 있어 자유롭게 호출되어져 버리기 때문에 유지보수성을 높이기 위해서라면 꼭 필요한 것만 public으로 해야 합니다.
3-2. 클래스 메소드
클래스 메소드(static 메소드)는 클래스에 속해 있는 메소드입니다. 자바에서는 클래스 메소드를 static 키워드를 붙여서 선언해요. 클래스 메소드는 필드 변수를 액세스 할 수 없습니다. 필드 변수의 관점에서 보면, 인스턴스 메소드를 클래스 메소드로 변경하는 경우에는 영향 받는 메소드가 줄어들게 되서 결과적으로 스코프가 작아지게 됩니다. 필드 변수에 액세스 할 필요가 없고 오버라이드 등이 필요가 없는 메소드는 클래스 메소드로 하는 편이 좋은 경우가 많습니다.
인스턴트 메소드
private int count; // 변수 count의 스코프 시작 private String regexGroup(String regex) { // 필드 변수에 액세스 가능 return "(" + regex + ")"; } // 변수 count의 스코프 끝
위 코드를 static 메소드로 변경
private int count; // 변수 count의 스코프 시작 . . . // 변수 count의 스코프 끝 private static String regexGroup(String regex) { // 필드 변수에 액세스 불가 return "(" + regex + ")"; } . . . // 변수 count의 스코프
4. 클래스의 스코프
4-1. 클래스의 스코프
자바에서 클래스의 스코프는 private, package private, protected, public 4종류이다. 클래스의 선언에서는 주로 public을 사용하는 경우가 많습니다. 그러나 프레임워크나 라이브러리를 작성하는 상황에서 package private를 사용하는 경우가 있습니다.
라이브러리 이용자에게 알리고 싶지 않은 클래스는 package private해 두면 나중에 클래스를 변경 할 때에도 그 변경의 영향을 이용자에게 주지 않게 할 수 있습니다. 즉 라이브러리 이용자가 알 필요 없는 클래스는 package private로 정리해 두면 됩니다. 그리고 private는 두 가지의 목적으로 사용되는데, 1. 인스턴스의 생성을 해당 클래스에서만 실시하게 하고 싶은 경우(디자인 패턴의 싱글톤 패턴 등)입니다. 하지만 메인 클래스는 private로 선언할 수 없기 때문에 생성자(constructor)을 private로 선언해서 다른 클래스 안에서는 인스턴스의 생성을 할 수 없게 해줍니다.

포스팅에 참고한 자료 : 좋은 코드를 작성하는 기술/아가타 토시타카, 정인식
'Java' 카테고리의 다른 글
(자바) 뮤직플레이어 프로그램 만들기(MVC패턴 적용 안한 상태) (1) | 2024.04.19 |
---|---|
[Java] this 키워드의 의미와 사용 방법 3가지(예시 코드 포함) (0) | 2024.04.19 |
[자바] 예시로 쉽게 이해하는 swtich-case문과 병합처리 (2) | 2024.03.29 |
[자바] 예시를 통해 쉽게 이해하는 이스케이프 시퀀스(Escape sequence)와 텍스트 블록(""") (3) | 2024.03.29 |
[java] 자바 배열 생성하는 4가지 방법과 배열 출력하기 (2) | 2024.03.21 |