본문 바로가기
Android/Compose

[Android] Compose에서 Word Wrap없이 글자 단위로 Text 표시하기

by 태크민 2025. 10. 26.

프로젝트를 진행하다 보면, 텍스트를 단어 단위가 아닌 글자 단위로 표시해야 할 때가 있습니다.

하지만, 기본적으로 안드로이드의 Text 컴포저블은 화면 끝에 긴 단어가 위치할 경우, 그 단어가 잘리지 않도록 자동으로 다음 줄로 이동시킵니다.

이러한 동작을 Word Wrap(또는 Line Wrap) 이라고 부릅니다.

 

즉, 내부 알고리즘에 따라 텍스트가 자동으로 줄바꿈되어 다음과 같이 표시됩니다.

자동으로 개행된 글자

 

하지만 경우에 따라서는 자동 개행 없이, 줄이 끝나더라도 글자 단위로 이어서 표시하고 싶을 때가 있습니다.


예를 들어, 아래와 같이 한 줄에 쭉 이어서 표시되는 형태를 원할 수도 있습니다.

자동으로 개행되지 않은 글자

 

안드로이드에서는 기본적으로 가독성을 높이기 위해 텍스트를 렌더링할 때 특정 기준에 따라 줄바꿈을 수행하는 알고리즘을 사용합니다.
그 결과, 위와 같은 자동 Word Wrap 현상이 발생하게 됩니다.

 

이 문제를 해결하기 위해 필자는 여러 가지 방법을 시도했습니다.
아래에서는 그 과정에서 시도했던 방법들과 각각의 특징에 대해 구체적으로 설명드리겠습니다.

 


첫 번째 시도: LineBreak API를 통한 줄바꿈 제어

가장 먼저 시도한 방법은 Android의 LineBreak API를 활용하는 방법이었습니다.

안드로이드에서는 기본적으로 텍스트의 가독성을 위해 내부적으로 줄바꿈 규칙을 적용하지만, 개발자가 이를 세부적으로 제어할 수 있도록 LineBreak API를 제공합니다.
따라서, 처음에는 이 속성을 조정하면 내가 원하는 “글자 단위 표시”를 구현할 수 있을 것이라 생각했습니다.

 

LineBreak API란?

LineBreak API는 텍스트가 여러 줄로 분리될 때 어떤 기준으로 줄을 나눌지 결정하는 규칙을 정의하는 API입니다.
Compose의 TextStyle 블록 내에서 설정할 수 있으며, 텍스트 유형(제목, 단락, 입력란 등) 에 따라 적절한 줄바꿈 방식을 지정할 수 있습니다.

 

안드로이드에서는 다음과 같은 세 가지 기본 LineBreak 유형을 제공합니다.

  • Simple → 가장 기본적이고 속도가 빠른(=CPU 부하가 적은) 줄바꿈 방식으로, 입력창(TextField) 등에 적합합니다.
  • Heading짧은 문장의 흐름을 자연스럽게 유지하도록 줄바꿈을 처리하는 방식으로, 제목이나 소제목 등에 적합합니다.
  • Paragraph → 긴 문단의 가독성을 높이기 위해 정교하고 품질이 높은 줄바꿈을 적용합니다.
  •  

아래는 Simple과 Paragraph 두 가지 방식의 줄바꿈 동작을 비교하는 예시입니다.

TextSample(
    samples = mapOf(
        "Simple" to {
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(130.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default.copy(
                    lineBreak = LineBreak.Simple
                )
            )
        },
        "Paragraph" to {
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(130.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default.copy(
                    lineBreak = LineBreak.Paragraph
                )
            )
        }
    )
)

  • Simple 모드: 빠르지만 단어 단위가 불균형하게 잘려, 텍스트의 시각적 균형이 다소 떨어집니다.
  • Paragraph 모드: 더 많은 연산을 수행하지만, 단어 단위가 부드럽게 나뉘며 훨씬 자연스럽고 읽기 편한 레이아웃을 제공합니다.

실제 출력에서도 Paragraph 모드가 Simple 모드보다 시각적으로 더 균형 잡힌 결과를 만들어내는 것을 확인할 수 있습니다.

 

더나아가, LineBreak 기본 모드(Simple, Heading, Paragraph) 외에도, Strategy, Strictness, WordBreak 매개변수를 통해 줄바꿈 규칙을 보다 세밀하게 제어할 수 있습니다.

 

1. Strategy — 줄바꿈 전략 제어

Strategy는 텍스트가 여러 줄로 나뉠 때, 어떤 방식으로 줄 길이와 하이픈(hyphen)을 조정할지를 정의합니다.
다음 세 가지 전략이 제공됩니다.


다음 세 가지는 텍스트 레이아웃 품질을 미세 조정할 때 유용한 고급 옵션입니다.

  • Balanced - 텍스트의 줄 길이를 균형 있게 조정하고 사용 설정된 경우 자동 하이픈 넣기를 적용합니다. 표시되는 텍스트의 양을 최대화하기 위해 시계와 같은 작은 화면에 권장됩니다.
  • HighQuality - 하이픈이 사용 설정된 경우를 포함하여 텍스트를 더 쉽게 읽을 수 있도록 단락을 최적화합니다. (Balanced 또는 Simple이 아닌 모든 항목의 기본값이어야 함)
  • Simple - 기본적이고 빠른 전략입니다. 사용 설정하면 한 줄에 들어가지 않는 단어에만 하이픈이 추가됩니다. 입력하는 동안 위치가 변경되지 않도록 텍스트를 수정하는 데 유용합니다.

다음 예시는 기본 설정이 적용된 텍스트와 Balanced 줄바꿈 Strategy를 사용하여 작은 화면에 최적화된 텍스트의 차이점을 보여줍니다.

TextSample(
    samples = mapOf(
        "Balanced" to {
            val smallScreenAdaptedParagraph =
                LineBreak.Paragraph.copy(strategy = LineBreak.Strategy.Balanced)
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(200.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default.copy(
                    lineBreak = smallScreenAdaptedParagraph
                )
            )
        },
        "Default" to {
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(200.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default
            )
        }
    )
)

 

추가로 Strictness 및 WordBreak API를 사용하여 LineBreak를 맞춤설정할 수도 있습니다. 이 API는 CJK 언어용으로 특별히 설계되었습니다. CJK 이외의 언어에서는 이러한 API의 효과가 항상 표시되지 않을 수 있습니다. 전반적으로 줄바꿈 규칙은 언어를 기반으로 정의됩니다.

 

2. Strictness — 줄바꿈 규칙의 엄격도 조정

Strictness는 어디에서 줄을 나눌 수 있는지에 대한 규칙을 정의합니다.
이 옵션은 특히 CJK(한중일) 언어를 다룰 때 유용하며, 언어별 기본값이 다를 수 있습니다.

  • Default - 언어의 기본 나누기 규칙입니다. Normal 또는 Strict에 해당할 수 있습니다.
  • Loose - 제한이 가장 적은 규칙입니다. 짧은 선에 적합합니다.
  • Normal - 줄바꿈에 가장 일반적인 규칙입니다.
  • Strict - 줄바꿈에 대한 가장 엄격한 규칙입니다

3. WordBreak — 단어 단위 줄바꿈 제어

WordBreak는 텍스트가 길어 한 줄에 다 들어가지 않을 때, 단어 내부나 구문 단위로 줄을 나눌지를 정의합니다.

WordBreak는 다음 속성을 사용하여 단어 내에 줄바꿈을 삽입하는 방법을 정의합니다.

  • Default - 언어의 기본 나누기 규칙입니다.
  • Phrase - 줄바꿈은 구문을 기반으로 합니다.

아래 예시는 일본어 텍스트에서 Strict(엄격한 줄바꿈)과 Phrase(구문 단위 줄바꿈)를 함께 적용한 코드입니다.

val customTitleLineBreak = LineBreak(
    strategy = LineBreak.Strategy.HighQuality,
    strictness = LineBreak.Strictness.Strict,
    wordBreak = LineBreak.WordBreak.Phrase
)
Text(
    text = "あなたに寄り添う最先端のテクノロジー。",
    modifier = Modifier.width(250.dp),
    fontSize = 14.sp,
    style = TextStyle.Default.copy(
        lineBreak = customTitleLineBreak
    )
)

 


두번째 시도. 공백을 특정 유니코드로 변경하기

앞서 살펴본 LineBreak API는 가독성을 위한 다양한 줄바꿈 제어 기능을 제공하지만,
디자이너와 제가 원한 것은 그것과는 조금 달랐습니다.

우리가 목표로 한 것은 줄의 중간에 공백이나 개행이 전혀 없이, 글자가 한 줄로 계속 이어지는 형태였습니다.


즉, 아래 예시처럼 단어 단위가 아닌 글자 단위로 텍스트를 표시하고 싶었던 것입니다.

이를 해결할 방법을 찾아보던 중, 여러 개발자들이 공백 문자를 ‘줄바꿈이 불가능한 공백 문자(Non-Breaking Space)’로 치환하는 방법을 사용하고 있다는 것을 확인했습니다.

 

즉, 일반 스페이스 대신 특정 유니코드 문자(\u00A0) 를 사용하는 방식입니다.

myString.replace(" ", "\u00A0")

 

이 \u00A0 문자는 줄바꿈이 허용되지 않는 공백을 의미하며,일반 공백처럼 보이지만 줄 끝에서도 자동 개행이 발생하지 않습니다.

실제로 적용해보면 텍스트가 단어 단위로 끊기지 않고 한 줄에 이어지는 형태로 표시되어, 처음에는 꽤 만족스러운 결과를 얻을 수 있었습니다.

 

하지만 이 마저도 100% 커버가 되지 않았습니다. 줄의 마지막에 공백이 들어가는 경우가 생겼고, 🕵️‍♀️👤🙇‍♀👩‍💻👨‍💻 🎧 🎙️와 같은 특수한 형태의 이모지가 들어간 경우 의도치 않게 띄어쓰기가 되는 경우가 생겼습니다.

 

결과적으로 \u00A0 치환 방식은 가장 간단하고 구현이 쉬운 접근법이었지만, 이모지나 복합문자, 줄 끝 공백 등 특수한 상황까지 완벽히 대응하기에는 한계가 있었습니다.

 


세번째 시도. Word Joiner(U+2060)로 의도치 않은 줄바꿈 완전 차단

여러 방법을 시도한 끝에, 글자 하나하나 사이마다 U+2060 Word Joiner(WJ) 문자를 넣는 방식이 가장 확실한 해결책이었습니다.


U+2060은 줄바꿈이 절대 일어나지 않도록 막는 특수한 보이지 않는 문자로, 이 문자를 글자 사이에 넣으면 그 지점에서는 절대 줄이 바뀌지 않습니다.

 

이 방법을 적용하니, 텍스트가 단어 단위가 아닌 글자 단위로 연속적으로 표시되었고, 이전 방법에서 문제가 되었던 이모지나 복합 문자에서도 의도치 않은 개행이 발생하지 않았습니다.

 

구현 코드 (Kotlin 확장함수)

아래는 문자열의 각 글자 사이에 Word Joiner를 자동으로 삽입하는 확장 함수입니다.
BreakIterator를 사용해 이모지, 한글 자모, 결합 문자 등도 “눈에 보이는 글자 단위”로 정확히 인식하도록 처리했습니다.

fun String.preventWordBreak(): String {
    if (isEmpty()) return this
    
    val breakIterator = java.text.BreakIterator.getCharacterInstance()
    breakIterator.setText(this)
    
    val result = StringBuilder()
    var start = breakIterator.first()
    var end = breakIterator.next()
    
    while (end != java.text.BreakIterator.DONE) {
        result.append(substring(start, end))
        
        // 마지막 grapheme가 아니면 Word Joiner 추가
        val nextEnd = breakIterator.next()
        if (nextEnd != java.text.BreakIterator.DONE) {
            result.append('\u2060')
        }
        
        start = end
        end = nextEnd
    }
    
    return result.toString()
}

 

이렇게 변환한 문자열을 Text 컴포저블에 전달하면, 줄바꿈이 일어나지 않고 글자가 한 줄로 자연스럽게 이어지게 됩니다.

 

왜 글자 사이에는 \u00A0이 아닌, \u2060(Word Joiner)를 사용해야 할까?

U+00A0(Non-Breaking Space, NBSP) 역시 줄바꿈을 막는 기능을 갖고 있지만, 이 문자는 기본적으로 공백(스페이스) 으로 인식됩니다.
따라서 이를 글자 사이에 넣으면 실제 화면에서도 글자 사이가 벌어져 보이는 현상이 생기게 됩니다.


즉, NBSP는 줄바꿈은 막지만 공백을 표시하는 문자입니다.결국 두 문자는 같은 “줄바꿈 방지” 기능을 가지고 있지만,
U+00A0는 “공백을 가지는 줄바꿈 방지 문자”,
U+2060은 “공백이 없는 줄바꿈 방지 문자”라는 차이가 있습니다.
따라서 글자 단위로 자연스럽게 이어 보이면서 줄바꿈만 막고 싶을 때는 U+2060을 사용하는 것이 적합합니다.

 

반면 U+2060(Word Joiner)은 제로폭(Zero-Width) 문자로,

시각적으로는 아무것도 표시되지 않으면서 해당 위치에서 줄바꿈을 완전히 차단합니다.
즉, 화면에는 글자 간격 변화가 전혀 없고, 글자들이 자연스럽게 이어진 채 줄바꿈만 방지됩니다.

 


마무리

여러 방법을 시도해본 결과, 안드로이드의 Text 컴포저블에서도 자동 개행이 발생하지 않는 글자 단위 표시를 완벽히 구현할 수 있었습니다.

LineBreak와 NBSP 등 다양한 방법을 검토했지만, 결국 Word Joiner(U+2060)를 활용한 방식이 가장 안정적이고 깔끔한 해결책이었습니다.

 

 

끝.

 


참고자료

https://developer.android.com/develop/ui/compose/text/style-paragraph?hl=ko#customize-line

 

단락에 스타일 지정  |  Jetpack Compose  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 단락에 스타일 지정 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 페이지에서는 단락의 텍스트

developer.android.com

https://ko.wikipedia.org/wiki/%EB%8B%A8%EC%96%B4_%EA%B2%B0%ED%95%A9%EC%9E%90

 

단어 결합자 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 단어 결합자(WJ: word joiner)는 유니코드의 제어 문자의 하나이다. 한국어와 달리 중국어 등 띄어쓰기를 하지 않는 언어에서는 문장 도중 임의의 위치에서 줄바꿈

ko.wikipedia.org

https://idoun.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-TextView%EC%97%90%EC%84%9C-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%9D%B4%EB%A3%A8%EC%96%B4%EC%A7%80%EB%8A%94-Word-wrap%EC%9D%84-%EB%A7%89%EB%8A%94-%EB%B0%A9%EB%B2%95

 

안드로이드 TextView에서 자동으로 이루어지는 Word wrap을 막는 방법

안드로이드 텍스트 뷰(TextView)는 화면 끝에 위치할 단어가 길어져 잘릴 것 같으면 그 단어를 자동으로 다음 줄로 줄바꿈 해 줍니다. 이 것을 Word wrap 또는 line wrap이라고 하죠. 그런데 표현할 뷰가

idoun.tistory.com

https://m.blog.naver.com/komgurrbs/221562426937

 

Android EditText 자동 줄 개행 제거하기

앱을 개발하면서 자동으로 문구가 개행되는 TextView나 EditText를 볼 수 있다. 이것을 Word wrap 또...

blog.naver.com