그림자는 UI를 시각적으로 높이고, 사용자에게 상호작용을 나타내며, 사용자 작업에 대한 즉각적인 피드백을 제공합니다. Compose는 앱에 그림자를 통합하는 여러 방법을 제공합니다.
Modifier.shadow()
: Material Design 가이드라인을 준수하는 컴포저블 뒤에 고도 기반 그림자를 만듭니다.Modifier.dropShadow()
: 컴포저블 뒤에 표시되어 컴포저블이 더 높게 보이도록 하는 맞춤설정 가능한 그림자를 만듭니다.Modifier.innerShadow()
: 컴포저블의 테두리 내부에 그림자를 만들어 뒤에 있는 표면으로 눌린 것처럼 보이게 합니다.
Modifier.shadow()
는 기본 그림자를 만드는 데 적합하며 dropShadow
및 innerShadow
수정자는 그림자 렌더링에 대한 더 세밀한 제어와 정밀도를 제공합니다.
이 페이지에서는 사용자 상호작용 시 그림자를 애니메이션으로 표시하는 방법, innerShadow()
및 dropShadow()
수정자를 연결하여 그라데이션 그림자, 뉴모피즘 그림자 등을 만드는 방법 등 이러한 각 수정자를 구현하는 방법을 설명합니다.
기본 그림자 만들기
Modifier.shadow()
는 Material Design 가이드라인을 따르는 기본 그림자를 만들어 위에서 오는 광원을 시뮬레이션합니다. 그림자 깊이는 elevation
값을 기반으로 하며 드리워진 그림자는 컴포저블의 모양으로 클리핑됩니다.
@Composable fun ElevationBasedShadow() { Box( modifier = Modifier.aspectRatio(1f).fillMaxSize(), contentAlignment = Alignment.Center ) { Box( Modifier .size(100.dp, 100.dp) .shadow(10.dp, RectangleShape) .background(Color.White) ) } }

Modifier.shadow
로 만든 고도 기반 그림자입니다.그림자 구현
dropShadow()
수정자를 사용하여 콘텐츠 뒤에 정확한 그림자를 그려 요소를 입체적으로 보이게 합니다.
Shadow
매개변수를 통해 다음 주요 측면을 제어할 수 있습니다.
radius
: 흐림의 부드러움과 확산을 정의합니다.color
: 색조의 색상을 정의합니다.offset
: x축과 y축을 따라 그림자의 도형을 배치합니다.spread
: 그림자 도형의 확장 또는 축소를 제어합니다.
또한 shape
매개변수는 그림자의 전체 모양을 정의합니다. androidx.compose.foundation.shape
패키지의 모든 도형과 Material Expressive shapes를 사용할 수 있습니다.
기본 그림자를 구현하려면 컴포저블 체인에 dropShadow()
수정자를 추가하여 반지름, 색상, 확산을 제공합니다. 그림자 위에 표시되는 purpleColor
배경은 dropShadow()
수정자 뒤에 그려집니다.
@Composable fun SimpleDropShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(300.dp) .dropShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 6.dp, color = Color(0x40000000), offset = DpOffset(x = 4.dp, 4.dp) ) ) .align(Alignment.Center) .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) ) { Text( "Drop Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
코드에 관한 핵심 사항
dropShadow()
수정자는 내부Box
에 적용됩니다. 그림자에는 다음과 같은 특성이 있습니다.- 모서리가 둥근 직사각형 모양 (
RoundedCornerShape(20.dp)
) 10.dp
의 블러 반경으로 가장자리가 부드럽고 확산됩니다.6.dp
의 확산으로 그림자 크기가 확대되어 그림자를 드리우는 상자보다 커집니다.0.5f
의 알파로 그림자를 반투명하게 만듭니다.
- 모서리가 둥근 직사각형 모양 (
- 그림자가 정의되면 .
background()
수정자가 적용됩니다.Box
가 흰색으로 채워져 있습니다.- 배경이 그림자와 동일한 둥근 직사각형 모양으로 잘립니다.
결과

내부 그림자 구현
dropShadow
의 반대 효과를 만들려면 Modifier.innerShadow()
를 사용하세요. 이 효과는 요소가 기본 표면에 움푹 들어가거나 눌린 듯한 착시를 만듭니다.
내부 그림자를 만들 때는 순서가 중요합니다. 내부 그림자는 콘텐츠의 상단에 그려지므로 일반적으로 다음을 수행해야 합니다.
- 배경 콘텐츠를 그립니다.
innerShadow()
수정자를 적용하여 오목한 모양을 만듭니다.
innerShadow()
가 배경 앞에 배치되면 배경이 그림자 위에 그려져 그림자가 완전히 숨겨집니다.
다음 예는 RoundedCornerShape
에 innerShadow()
를 적용한 경우를 보여줍니다.
@Composable fun SimpleInnerShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) // note that the background needs to be defined before defining the inner shadow .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) .innerShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 2.dp, color = Color(0x40000000), offset = DpOffset(x = 6.dp, 7.dp) ) ) ) { Text( "Inner Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }

Modifier.innerShadow()
을 적용한 모습사용자 상호작용 시 그림자 애니메이션
사용자 상호작용에 그림자가 반응하도록 하려면 그림자 속성을 Compose의 애니메이션 API와 통합하면 됩니다. 예를 들어 사용자가 버튼을 누르면 그림자가 변경되어 즉각적인 시각적 피드백을 제공할 수 있습니다.
다음 코드는 그림자를 사용하여 '눌림' 효과를 만듭니다 (표면이 화면으로 아래로 밀리는 착시).
@Composable fun AnimatedColoredShadows() { SnippetsTheme { Box(Modifier.fillMaxSize()) { val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() // Create transition with pressed state val transition = updateTransition( targetState = isPressed, label = "button_press_transition" ) fun <T> buttonPressAnimation() = tween<T>( durationMillis = 400, easing = EaseInOut ) // Animate all properties using the transition val shadowAlpha by transition.animateFloat( label = "shadow_alpha", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) 0f else 1f } // ... val blueDropShadow by transition.animateColor( label = "shadow_color", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) Color.Transparent else blueDropShadowColor } // ... Box( Modifier .clickable( interactionSource, indication = null ) { // ** ...... **// } .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = blueDropShadow, offset = DpOffset(x = 0.dp, -(2).dp), alpha = shadowAlpha ) ) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = darkBlueDropShadow, offset = DpOffset(x = 2.dp, 6.dp), alpha = shadowAlpha ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color(0xFFFFFFFF), shape = RoundedCornerShape(70.dp) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 8.dp, spread = 4.dp, color = innerShadowColor2, offset = DpOffset(x = 4.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 20.dp, spread = 4.dp, color = innerShadowColor1, offset = DpOffset(x = 4.dp, 0.dp), alpha = innerShadowAlpha ) ) ) { Text( "Animated Shadows", // ... ) } } } }
코드에 관한 핵심 사항
transition.animateColor
및transition.animateFloat
를 사용하여 누를 때 애니메이션을 적용할 매개변수의 시작 및 종료 상태를 선언합니다.updateTransition
를 사용하고 선택한targetState (targetState = isPressed)
를 제공하여 모든 애니메이션이 동기화되었는지 확인합니다.isPressed
가 변경될 때마다 전환 객체는 현재 값에서 새 타겟 값으로 모든 하위 속성의 애니메이션을 자동으로 관리합니다.- 전환의 타이밍과 이징을 제어하는
buttonPressAnimation
사양을 정의합니다. 지속 시간이 400밀리초이고EaseInOut
곡선이 있는tween
(중간의 약자)을 지정합니다. 즉, 애니메이션이 느리게 시작되고 중간에 빨라지며 끝에 느려집니다. - 모든 애니메이션 속성을 적용하여 시각적 요소를 만드는 수정자 함수 체인이 있는
Box
를 정의합니다. 여기에는 다음이 포함됩니다.- .
clickable()
:Box
를 대화형으로 만드는 수정자입니다. .dropShadow()
: 두 개의 외부 그림자가 먼저 적용됩니다. 색상 및 알파 속성은 애니메이션 값 (blueDropShadow
등)에 연결되어 초기 돌출된 모양을 만듭니다..innerShadow()
: 배경 위에 두 개의 내부 그림자가 그려집니다. 속성은 다른 애니메이션 값(innerShadowColor1
등)과 연결되어 들여쓰기된 모양을 만듭니다.
- .
결과
그라데이션 그림자 만들기
그림자는 단색으로 제한되지 않습니다. 그림자 API는 Brush
를 허용하므로 그라데이션 그림자를 만들 수 있습니다.
Box( modifier = Modifier .width(240.dp) .height(200.dp) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = animatedSpread.dp, brush = Brush.sweepGradient( colors ), offset = DpOffset(x = 0.dp, y = 0.dp), alpha = animatedAlpha ) ) .clip(RoundedCornerShape(70.dp)) .background(Color(0xEDFFFFFF)), contentAlignment = Alignment.Center ) { Text( text = breathingText, color = Color.Black, style = MaterialTheme.typography.bodyLarge ) }
코드에 관한 핵심 사항
dropShadow()
는 상자 뒤에 그림자를 추가합니다.brush = Brush.sweepGradient(colors)
는 사전 정의된colors
목록을 통해 회전하는 그라데이션으로 그림자를 색칠하여 무지개와 같은 효과를 만듭니다.
결과
브러시를 그림자로 사용하여 '숨쉬는' 애니메이션이 있는 그라데이션 dropShadow()
을 만들 수 있습니다.
그림자 결합
dropShadow()
및 innerShadow()
수정자를 결합하고 레이어링하여 다양한 효과를 만들 수 있습니다. 다음 섹션에서는 이 기법을 사용하여 뉴모피즘, 네오브루탈리즘, 사실적인 그림자를 만드는 방법을 보여줍니다.
뉴모피즘 그림자 만들기
뉴모픽 그림자는 배경에서 자연스럽게 나타나는 부드러운 모양이 특징입니다. 뉴모피즘 그림자를 만들려면 다음을 실행하세요.
- 배경과 동일한 색상을 공유하는 요소를 사용합니다.
- 두 개의 희미한 반대 그림자를 적용합니다. 한쪽 모서리에는 밝은 그림자를, 반대쪽 모서리에는 어두운 그림자를 적용합니다.
다음 스니펫은 두 개의 dropShadow()
수정자를 레이어링하여 뉴모피즘 효과를 만듭니다.
@Composable fun NeumorphicRaisedButton( shape: RoundedCornerShape = RoundedCornerShape(30.dp) ) { val bgColor = Color(0xFFe0e0e0) val lightShadow = Color(0xFFFFFFFF) val darkShadow = Color(0xFFb1b1b1) val upperOffset = -10.dp val lowerOffset = 10.dp val radius = 15.dp val spread = 0.dp Box( modifier = Modifier .fillMaxSize() .background(bgColor) .wrapContentSize(Alignment.Center) .size(240.dp) .dropShadow( shape, shadow = Shadow( radius = radius, color = lightShadow, spread = spread, offset = DpOffset(upperOffset, upperOffset) ), ) .dropShadow( shape, shadow = Shadow( radius = radius, color = darkShadow, spread = spread, offset = DpOffset(lowerOffset, lowerOffset) ), ) .background(bgColor, shape) ) }

네오브루탈리즘 그림자 만들기
네오브루탈리스트 스타일은 고대비, 블록형 레이아웃, 선명한 색상, 두꺼운 테두리를 보여줍니다. 이 효과를 만들려면 다음 스니펫과 같이 블러가 0이고 오프셋이 명확한 dropShadow()
를 사용하세요.
@Composable fun NeoBrutalShadows() { SnippetsTheme { val dropShadowColor = Color(0xFF007AFF) val borderColor = Color(0xFFFF2D55) Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(0.dp), shadow = Shadow( radius = 0.dp, spread = 0.dp, color = dropShadowColor, offset = DpOffset(x = 8.dp, 8.dp) ) ) .border( 8.dp, borderColor ) .background( color = Color.White, shape = RoundedCornerShape(0.dp) ) ) { Text( "Neobrutal Shadows", modifier = Modifier.align(Alignment.Center), style = MaterialTheme.typography.bodyMedium ) } } } }

사실적인 그림자 만들기
사실적인 그림자는 실제 세계의 그림자를 모방합니다. 기본 광원에 의해 조명되는 것처럼 보여 직접 그림자와 더 확산된 그림자가 모두 생성됩니다. 다음 스니펫과 같이 속성이 다른 dropShadow()
및 innerShadow()
인스턴스를 여러 개 쌓아 현실적인 그림자 효과를 재현할 수 있습니다.
@Composable fun RealisticShadows() { Box(Modifier.fillMaxSize()) { val dropShadowColor1 = Color(0xB3000000) val dropShadowColor2 = Color(0x66000000) val innerShadowColor1 = Color(0xCC000000) val innerShadowColor2 = Color(0xFF050505) val innerShadowColor3 = Color(0x40FFFFFF) val innerShadowColor4 = Color(0x1A050505) Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 40.dp, spread = 0.dp, color = dropShadowColor1, offset = DpOffset(x = 2.dp, 8.dp) ) ) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 0.dp, color = dropShadowColor2, offset = DpOffset(x = 0.dp, 4.dp) ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color.Black, shape = RoundedCornerShape(100.dp) ) // // .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 3.dp, color = innerShadowColor1, offset = DpOffset(x = 6.dp, 6.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 1.dp, color = Color.White, offset = DpOffset(x = 5.dp, 5.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 5.dp, color = innerShadowColor2, offset = DpOffset(x = (-3).dp, (-12).dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 10.dp, color = innerShadowColor3, offset = DpOffset(x = 0.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 9.dp, color = innerShadowColor4, offset = DpOffset(x = 1.dp, 1.dp) ) ) ) { Text( "Realistic Shadows", modifier = Modifier.align(Alignment.Center), fontSize = 24.sp, color = Color.White ) } } }
코드에 관한 핵심 사항
- 속성이 서로 다른 두 개의 연결된
dropShadow()
수정자가 적용된 후background
수정자가 적용됩니다. - 연결된
innerShadow()
수정자는 구성요소의 가장자리 주위에 금속 테두리 효과를 적용하는 데 사용됩니다.
결과
이전 코드 스니펫은 다음을 생성합니다.
