Secure Text Entry는 Android에서 입력된 문자가 실제 문자 대신 숨겨진 형태(점 또는 별표)로 표시되는 텍스트 입력 모드입니다. 이 모드는 비밀번호 필드, PIN 코드, CVV 코드 및 기타 기밀 정보에 사용됩니다. Secure Text Entry는 시각적 가로채기로부터 데이터를 보호할 뿐만 아니라 스크린샷, 화면 녹화 및 자동 수정 기능을 통한 유출도 방지합니다. Android Security Best Practices (2026)에 따르면, 사용자 인증 데이터를 처리하는 애플리케이션에는 숨김 입력이 필수 요구 사항입니다.
핵심 요점
Secure Text Entry(보호된 텍스트 입력)는 입력된 각 문자의 표시를 마스킹 문자(일반적으로 점(•) 또는 별표(*))로 대체하는 Android 기능입니다. 이는 어깨 너머로 비밀번호를 읽는 행위(shoulder surfing)를 방지하고 입력된 데이터를 엿보는 눈으로부터 숨깁니다. 이 기능은 비밀번호 마스킹이라고도 하며 모바일 애플리케이션의 표준 관행입니다.
iOS에서 Secure Text Entry가 별도의 UITextField 설정인 것과 달리, Android에서는 이 기능이 PASSWORD 변형과 함께 inputType 속성을 통해 활성화됩니다. EditText가 inputType="textPassword"를 받으면 자동으로 PasswordTransformationMethod(문자 대체를 담당하는 클래스)를 적용합니다. 각 새 문자가 입력될 때마다 EditText는 TransformationMethod 인터페이스 메커니즘을 통해 즉시 마스킹 문자로 대체합니다.
Android Source Code (AOSP, 2026)에 따르면, PasswordTransformationMethod의 구현은 TextPaint 클래스를 사용하여 마스킹 문자를 렌더링합니다. 마스킹 문자의 크기는 필드의 글꼴 크기와 일치하여 단어 길이를 통한 정보 유출을 방지합니다 — 모든 문자의 너비가 동일하게 보입니다. 이는 입력된 데이터의 길이 분석에 기반한 공격으로부터 보호하는 데 중요합니다.
<!-- Basic Secure Text Entry example -->
@+id/etPassword
android:layout_width="match_parent"
android:hint="Enter password"
android:inputType="textPassword"
android:maxLength="64"
android:importantForAutofill="yes" />
Android의 Secure Text Entry 메커니즘은 android.text.method 패키지에 정의된 TransformationMethod 인터페이스를 기반으로 합니다. PasswordTransformationMethod는 getTransformation() 메서드를 재정의하여 표시용으로 대체된 문자 시퀀스를 반환하는 내장 구현입니다. Editable의 원본 텍스트는 변경되지 않으며 — 시각적 표현만 변경됩니다.
사용자가 문자를 입력하면 EditText가 dispatchEvent를 호출하고 문자를 InputConnection에 전달하여 Editable에 추가합니다. 그런 다음 EditText는 현재 TransformationMethod의 getTransformation() 메서드를 호출하여 원본 CharSequence를 가져와 마스킹 문자가 포함된 SpannableString을 반환합니다. 이 SpannableString이 원본 텍스트 대신 화면에 렌더링됩니다. 주요 특징은 변환 자체가 Editable 내용을 변경하지 않으므로 getText().toString()을 호출하면 실제 입력된 텍스트가 반환된다는 것입니다.
Android Developers Reference (2026)에 따르면, PasswordTransformationMethod는 각 문자에 MetricAffectingSpan을 추가하여 글리프를 점으로 대체합니다. 이러한 스팬은 텍스트 길이에 영향을 주지 않으므로 숨김 표시 상태에서도 커서와 입력 위치가 올바르게 유지됩니다. VisiblePasswordTransformationMethod도 있어 마지막 입력 문자를 짧은 시간 동안 표시합니다(iOS와 유사).
// PasswordTransformationMethod - built-in masking
editText.transformationMethod = PasswordTransformationMethod.getInstance()
// VisiblePasswordTransformationMethod - shows last char
editText.transformationMethod = VisiblePasswordTransformationMethod.getInstance()
표시 토글(password visibility toggle)은 사용자가 양식을 제출하기 전에 입력한 비밀번호를 임시로 확인할 수 있는 기능입니다. Android에서는 app:endIconMode="password_toggle" 속성이 있는 TextInputLayout을 통해 이 기능이 구현됩니다. 눈 아이콘을 탭하면 필드가 textPassword와 textVisiblePassword 사이에서 전환됩니다.
TextInputLayout의 password_toggle 구현은 내부 EditText의 inputType을 자동으로 변경하고 아이콘을 애니메이션화합니다(눈 열기/닫기). 이러한 전환 중에 커서 위치가 재설정될 수 있으므로 TextInputLayout은 모드 변경 후 위치를 저장하고 복원합니다. 또한 표시 비밀번호로 전환할 때 자동 수정 및 제안이 활성화되지 않습니다 — 일반 텍스트가 아닌 TYPE_TEXT_VARIATION_VISIBLE_PASSWORD가 사용됩니다.
Material Design Guidelines (2025)에 따르면, 비밀번호 표시 아이콘은 필드가 포커스될 때뿐만 아니라 항상 표시되어야 합니다. 사용자는 언제든지 비밀번호를 확인할 수 있어야 합니다. 아이콘은 일정 시간 후에 자동으로 비밀번호를 숨기지 않아야 합니다 — 사용자 작업에 의해서만 가능합니다.
<!-- TextInputLayout with password toggle -->
@+id/tilPassword
android:layout_width="match_parent"
app:endIconMode="password_toggle"
app:passwordToggleTint="@color/primary">
@+id/etPassword
android:inputType="textPassword"
android:hint="Password" />
</com.google.android.material.textfield.TextInputLayout>
Kotlin에서 Secure Text Entry의 프로그래밍 설정은 inputType을 설정하거나 setTransformationMethod()를 통해 수행됩니다. 두 접근 방식 모두 동일한 결과를 가져오지만 세부 사항이 다릅니다. inputType을 설정하면 자동 수정 및 제안도 추가로 구성되는 반면, setTransformationMethod는 문자 표시에만 영향을 줍니다.
// Programmatic password visibility toggle
fun togglePasswordVisibility(til: TextInputLayout) {
val editText = til.editText ?: return
val currentType = editText.inputType
val isPassword = (currentType and
InputType.TYPE_TEXT_VARIATION_PASSWORD) != 0
if (isPassword) {
editText.inputType = InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
} else {
editText.inputType = InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_PASSWORD
}
// Restore cursor position
val cursorPos = editText.selectionStart
editText.setSelection(maxOf(0, cursorPos))
}
// Custom masking — shows only last 4 chars
class PartialPasswordTransformation : TransformationMethod {
override fun getTransformation(source: CharSequence?,
view: View?): CharSequence {
val text = source?.toString() ?: ""
if (text.length <= 4) return text
val dots = "\u2022".repeat(text.length - 4)
return dots + text.substring(text.length - 4)
}
override fun onFocusChanged(view: View?,
sourceText: CharSequence?,
focused: Boolean,
direction: Int,
previouslyFocusedRect: android.graphics.Rect?) {}
}
Secure Text Entry는 여러 수준에서 보호를 제공합니다. 시각적 마스킹은 어깨 너머 비밀번호 읽기(shoulder surfing)를 방지합니다 — 공공 장소에서 비밀번호 도난의 가장 일반적인 방법입니다. 자동 수정을 비활성화하면 비밀번호가 사용자 사전에 저장되는 것을 방지하여 예측 입력을 통한 유출을 차단합니다. 스크린샷 보호(FLAG_SECURE가 활성화된 경우)는 다른 애플리케이션이 필드 내용을 캡처하는 것을 방지합니다.
그러나 Secure Text Entry는 완전한 보호가 아닙니다. 시스템 수준에서 입력 가로채기(키로깅) 공격은 InputConnection에서 실제 문자를 얻을 수 있습니다. 접근성 서비스가 있는 악성 애플리케이션은 화면에 문자가 숨겨져 있어도 EditText 내용을 읽을 수 있습니다. 또한 적절한 인증서 검증과 함께 HTTPS를 사용하지 않는 경우 서버로 데이터 전송 중 SSL 스니핑으로 비밀번호가 노출될 수 있습니다.
OWASP Mobile Security Testing Guide (2025)에 따르면, Android에서 최대 비밀번호 보호를 위해 권장되는 사항은 다음과 같습니다: 중요한 필드에는 inputType="textPassword" + autofill 비활성화; 필요한 것보다 오래 메모리에 비밀번호를 캐시하지 않음; 애플리케이션 최소화 시 비밀번호 필드 지우기(onPause/onStop); 비밀번호 짧은 표시 중에도 마스킹 사용.
| 보호 수준 | Secure Text Entry | 추가 조치 |
|---|---|---|
| 시각적 가로채기 | 문자 마스킹 | 생체 인증, shoulder surfing 방지 |
| 스크린샷 | 부분적(구현에 따라 다름) | Window에 FLAG_SECURE |
| 키로깅 | 보호하지 않음 | 안전한 입력이 있는 IME 사용 |
| 자동 수정/사전 | 비활성화 | 비밀번호에 대해 기본적으로 비활성화 |
| 네트워크 가로채기 | 보호하지 않음 | 인증서 고정이 있는 HTTPS |
Android 개발자는 숨김 입력을 작업할 때 몇 가지 핵심 관행을 따라야 합니다. 첫째, 비밀번호 필드에는 항상 endIconMode="password_toggle"이 있는 TextInputLayout을 사용하세요 — 이것이 현대 Material Design 표준입니다. 둘째, 비밀번호를 표시하는 기능을 비활성화하지 마세요 — 사용자는 특히 작은 키보드가 있는 모바일 장치에서 입력한 내용을 확인할 수 있어야 합니다.
Secure Text Entry는 적절한 자동 채우기 구성도 필요합니다. 비밀번호 필드의 경우 비밀번호 관리자가 데이터를 올바르게 채우고 저장할 수 있도록 항상 autofillHints="password" 또는 autofillHints="newPassword"를 지정하세요. 새 비밀번호 확인 필드의 경우 autofillHints="newPassword"를 사용하여 시스템이 현재 저장된 비밀번호로 채우는 것을 제안하지 않도록 합니다.
// Full password field with autofill and cleanup
class SecurePasswordField {
private fun setupPasswordField(til: TextInputLayout) {
val editText = til.editText ?: return
editText.inputType = InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_PASSWORD
editText.setAutofillHints(View.AUTOFILL_HINT_PASSWORD>)
editText.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
}
fun clearOnPause(editText: EditText) {
editText.text?.clear()
}
}
자주 묻는 질문
textPassword는 입력된 모든 문자를 숨기고 자동 수정을 비활성화합니다. textVisiblePassword도 자동 수정을 비활성화하지만 문자를 명확하게 표시합니다. textVisiblePassword는 비밀번호 표시를 토글할 때 사용됩니다 — 보안을 위해 자동 수정 및 제안이 비활성화된 상태로 유지되므로 일반 텍스트가 아닙니다.
네, TransformationMethod 인터페이스를 구현하는 사용자 정의 클래스를 만드세요. 원하는 대체 문자가 포함된 CharSequence를 반환하는 getTransformation() 메서드를 재정의하세요. editText.transformationMethod = CustomPasswordTransformation()을 통해 설정합니다. 보안을 위해 문자는 고정폭이어야 합니다.
작은 화면과 터치 키보드가 있는 모바일 장치에서는 비밀번호 입력 시 오류 가능성이 데스크톱보다 훨씬 높습니다. 입력한 비밀번호를 확인할 수 있는 기능은 입력 오류를 줄이고 비밀번호 재설정 요청 수를 감소시켜 사용자 경험을 개선하고 지원 부하를 줄입니다.
Activity 또는 Dialog 창에 FLAG_SECURE 플래그를 설정하세요: window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE). 이렇게 하면 전체 창에 대해 스크린샷과 화면 녹화가 방지됩니다. 하지만 일반 콘텐츠에 대한 스크린샷도 비활성화되므로 바람직하지 않을 수 있습니다.
Android 8.0(API 26)부터 Autofill Framework는 inputType을 통해 비밀번호 필드를 자동으로 감지합니다. 올바른 작동을 위해 importantForAutofill="yes"를 설정하고 autofillHints("password", "newPassword", "emailAddress")를 지정하세요. Gboard와 비밀번호 관리자는 이러한 속성을 사용하여 저장된 비밀번호를 제안합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.