- Branch

Repository의 공간에서 독립적으로 어떤 작업을 하기 위한 공간을 만듦

 

- Clone

원격 저장소로부터 소스코드를 로컬 저장소로 복제한다

 

- Commit

로컬 저장소에서 변경사항을 저장 (원격 저장소에 변경사항을 반영하기 전 단계) 

 

- Push

Commit된 변경내용을 원격 저장소에 반영

 

- Fetch

원격 저장소에서 변경된 Meta-Data를 확인 (변경된 데이터를 확인만 하고 로컬 저장소로 가져오지 않음)

 

- Pull

원격 저장소의 내용을 로컬 저장소에 반영하는 것 (fetch + merge)

 

- Rebase

2개의 브랜치를 합칠 때 사용, 브랜치의 변경 사항을 순서대로 다른 브랜치에 적용하면서 합침

 

- Pull Request

브랜치에서 완료된 작업을 리뷰하고, 마스터로 합치도록 요청하는 명령어

 

- Checkout

브랜치를 변경하는 명령어

'Android개발 > Git' 카테고리의 다른 글

Git 명령어 모음  (0) 2025.05.30

XML (EditText) -> Compose (TextField, OutlinedTextField) 두개로 변경이 되었다.

두개로 나눈 이유는 디자인 스타일과 사용자 경험을 다르게 주기 위함이라고 한다.

 

두개의 차이가 무엇인지 확인 해 보자

TextFieldOutlinedTextField

설정 지정 없이 디버깅 했을 때 왼쪽이 TextField, 오른쪽이 OutlinedTextField이다. 

 

왼쪽은 배경에 색상이 들어가있고 오른쪽엔 Outline이 그려져 있는것을 볼 수 있다. 해당 Field중 선호하는것을 사용하면 될 듯하지만

강조하고 싶은 느낌을 줄 때는 TextField, 깔끔하고 가독성이 필요한 경우엔 오른쪽이 적합해 보인다.

 

하지만 우린 과거에도 그랬고 미래에도 그럴 것이다. 

우리는 디자인을 입혀야 하기때문에 개인 앱이 아닌 이상 저대로 사용할리가 만무하다.

그럼 우리가 할일은 무엇일까?

커스텀하기 좋게 평범한 입력창을 만들어야 한다. 

 

자 그럼 어느것을 사용해야 할지 감이 잘 안올것이다.

우린 커스텀을 하기 위해선 백지로 돌아가야한다. 그럼 백지에 가장 가까운 것은 무엇인가?

바로 OutlinedTextField이다.

 

OutlinedTextField는 다음과 같은 이유로 커스텀을 하기가 쉽다.

1. 구조가 단순함 - 배경이 없고 테두리만 있어서, 커스텀 스타일 넣기가 쉽다

2. 테두리 조정 쉬움 - 속성 수정이 명확하기 때문에 원하는 값을 넣어 조정할 수 있다.

3. 배경 간섭이 없다 - TextField는 배경색이 들어가있지만 이 친구는 흰색이다.

4. 구글 샘플 디자인 예시에서도 OutlinedTextField가 더 많이 사용된다. 

 

그럼 OutlinedTextField에 속성에 대해 학습해 보자

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    var text by remember { mutableStateOf(name) }

    Box(modifier = Modifier.fillMaxWidth().fillMaxHeight(),
        contentAlignment = Alignment.Center
    ) {
        OutlinedTextField(
            //현재 입력된 텍스트 값
            value = text,
            //텍스트가 바뀔 때 마다 실행되는 람다
            onValueChange = { text = it },
            //입력창 위에 표시되는 라벨 텍스트
            label = { Text("이름") },
            //입력 전 회색 안내 텍스트 (EditText: hint)
            placeholder = { Text("이름을 입력 해 주세요.") },
            //왼쪽에 아이콘 추가
            leadingIcon = { Icon(Icons.Default.Person, contentDescription = null)},
            //오른쪽에 아이콘 추가
            trailingIcon = {
                if (text.isNotEmpty()) {
                    IconButton(onClick = { text = ""}) {
                        Icon(Icons.Default.Close, contentDescription = "Clear")
                    }
                }
            },
            //에러 상태 여부
            isError = text.isEmpty(),
            //한 줄 입력만 가능
            singleLine = true,
            //테두리 모양 (라운드 설정)
            shape = RoundedCornerShape(4.dp),
            //텍스트 스타일 지정
            textStyle = TextStyle(fontSize = 16.sp, color = Color.Black),
            // 색상 커스터마이징
            colors = OutlinedTextFieldDefaults.colors(
                // 포커스 있을 때 테두리 색
                focusedBorderColor = Color.Blue,
                // 포커스 없을 때 테두리 색
                unfocusedBorderColor = Color.Gray,
                // 에러 상태일 때 테두리 색
                errorBorderColor = Color.Red,
                // 커서 색상
                cursorColor = Color.Blue
            ),
            modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
        )
    }
}

텍스트가 비어있으면 에러상태로 설정이 가능하고 XML에 있던 EditText보다 더 유연해진 느낌이다.

그럼 속성값을 분석 해 보자

 

속성 설명
value 초기에 자동으로 입력될 텍스트 값 (이전에 입력된 값을 다시 입력해 줄 때 사용)
onValueChange 텍스트가 바뀔 때 마다 실행되는 람다 (입력되는 값을 변수에 저장할 때 사용)
label 입력 창 위에 표시되는 라벨 텍스트 (설정할 일이 별로 없을 것 같음)
placeholder 입력 전 회색 안내 텍스트(EditText에서 hint역할)
hint color, fontSize등 커스텀 가능

placeholder = { 
Text(
    text="이름을 입력 해 주세요.",
    fontSize = 11.sp,
    color = Color.Blue
    ) 
}

leadingIcon 입력 창 왼쪽에 아이콘 추가
trailingIcon 입력 창 오른쪽에 아이콘 추가
isError 에러 상태 여부 (입력 되면 안되는 값을 넣거나 텍스트가 비었을 때 에러 상태라는 것을 표시해주는 용도로 추정)
singleLine 한 줄 입력만 가능 
shape 테두리 모양 설정 (라운드 설정)
textStyle 입력되는 텍스트 스타일 지정 (폰트 크기 및 색상 등)
colors 색상 커스텀 (포커스 여부에 따른 테두리 색상 변경 및 커서 색상 등)

 

위의 상태에서 테두리도 없고 입력창과 힌트만 있는 TextField를 구현하던가 테두리만 있고 입력창과 힌트만 있는 TextField를 구현해 보자

WebView Setting 속성 정리

@SuppressLint("SetJavaScriptEnabled")
private fun webViewSetting(webView: WebView) {

    val webSettings = webView.settings

    webSettings.apply {
        //기본 설정
        
        //자바 스크립트 사용 허용
        javaScriptEnabled = true   
        //컨텐츠 크기에 맞게 화면 맞춤
        loadWithOverviewMode = true 
        //뷰 포트 설정
        useWideViewPort = true  
        //DOM저장소 사용
        domStorageEnabled = true    
        //파일 접근 허용
        allowFileAccess = true  
        //자바 스크립트로 새 창 열기 허용
        javaScriptCanOpenWindowsAutomatically = true  
        //멀티 윈도우 지원
        setSupportMultipleWindows(true) 
        //file://로 로드된 로컬HTML파일이 다른 로컬파일에 접근 할 수 있게 허용
        allowFileAccessFromFileURLs = true  
        //캐시 사용 기본 설정
        cacheMode = WebSettings.LOAD_DEFAULT    
        //웹 페이지의 텍스트와 레이아웃을 어떻게 배치 할지 설정
        layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS   
        //플러그인 자동 실행 여부
        pluginState = WebSettings.PluginState.ON    
        //HTTPS페이지 안에서 HTTP콘텐츠 허용 여부
        mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW   
        //비디오나 오디오 자동 재생 여부 (false가 자동 재생 허용)
        mediaPlaybackRequiresUserGesture = false    
        //웹 콘텐츠를 Chrome개발자 도구로 디버깅 할 수 있게 해주는 설정(inspect)
        WebView.setWebContentsDebuggingEnabled(false)  

	//웹페이지의 이미지를 자동으로 불러올지 여부를 설정
        loadsImagesAutomatically = true 
        //웹 페이지의 기본 문자 인코딩 방식 지정
        defaultTextEncodingName = "utf-8"   
        
        //줌 관련
        //줌 기능 허용
        setSupportZoom(true)  
        //줌 컨트롤 사용
        builtInZoomControls = true  
        //줌 컨트롤 UI숨김
        displayZoomControls = false 
    }

}

private fun setScrollBarDisable(webView: WebView) {
    webView.apply {
    	//수직 스크롤바 노출 여부
        isVerticalScrollBarEnabled = false  
        //수직 스크롤바 웹뷰 내용 위에 겹쳐서 표시 노출 여부
        setVerticalScrollbarOverlay(false) 
        //수평 스크롤 바 노출 여부
        isHorizontalScrollBarEnabled = false   
        //수평 스크롤바 웹뷰 내용 위에 겹쳐서 노출 여부
        setHorizontalScrollbarOverlay(false)    
    }
}

 

javaScriptEnabled 

true: 자바 스크립트 코드 실행 가능

false: 자바 스크립트 실행 불가능

 

loadWithOverviewMode

true: 웹페이지가 화면 크기에 맞춰 전체가 보이도록 축소되서 로드

false: 웹페이지 원래 크기로 로드되고, 사용자가 스크롤 하거나 확대/축소해야 전체를 볼 수 있음

 

useWideViewPort

true: <meta name="viewport" ..> 태그 인식 가능, 웹페이지 레이아웃을 디바이스 화면 너비에 맞게 조정

false(default): 기본 너비를 사용해 페이지 렌더링

 

domStorageEnabled

true: WebView내의 HTML5의 로컬 스토리지와 세션 스토리지 같은 DOM Storage기능 활성화

false(default): DOM Storage기능 비활성화, 일부 기능 제한 

 

** DOM Storage란 웹페이지가 데이터를 브라우저 안에 저장할 수 있도록 해주는 기능이다.

localStorage와 sessionStorage두가지 종류가 있으며

localStorage는 창을 닫아도 데이터가 남아 있다 (반 영구적)

sessionStorage는 탭을 닫으면 데이터가 사라짐(세션 동안만 유지)

 

allowFileAccess

true: 웹 페이지가 기기의 내부 파일에 접근 할 수 있게 허용 (file://주소의 파일을 읽을 수 있게 허용)

false: 웹페이지가 기기의 내부 파일에 접근 할 수 있게 허용하지 않음

*보안 주의 -> 외부에서 불러온 웹사이트인 경우엔 false권장

 

javaScriptCanOpenWindowsAutomatically

true: 자바스크립트가 새 창을 자동으로 열 수있게 허용 (javascript -> window.open() 허용)

false(default): 자바스크립트가 새 창을 자동으로 열 수 없음

 

** 주의  광고나 악성 스크립트가 사용자 몰래 팝업을 열 수도 있음

 

setSupportMultipleWindows

true: 여러 창을 호출할 수 있게 지원 해줌 (javascript -> window.open() 실제 실행, javaScriptCanOpenWindowsAutomatically와 함께 true로 설정해야 함)

false(default): 여러 창을 호출할 수 없음

 

allowFileAccessFromFileURLs

true: file://주소로 로드된 웹페이지가 다른 파일에도 접근 가능 (신뢰된 페이지에서만 사용 권장) 

false(default): 접근 불가능 

 

cacheMode

웹페이지 데이터를 캐시에서 불러올지, 네트워크에서 새로 받을지 결정하는 설정 

WebSettings.LOAD_DEFAULT 

-> 기본 설정, 인터넷이 연결되어 있으면 항상 새로 로드하고 연결되어 있지 않은 경우에는 캐시 사용

WebSettings.NO_CACHE (항상 최신 데이터를 보여줘야 하는 경우)

-> 캐시 사용 X

WebSettings.LOAD_CACHE_ELSE_NETWORK (오프라인에서도 앱이 잘 동작해야 하는 경우)

-> 캐시에 데이터가 있는 경우 캐시 사용, 없는 경우 네트워크 요청

WebSettings.LOAD_CACHE_ONLY (오프라인에서도 앱이 잘 동작해야 하는 경우)

-> 항상 캐시만 사용 네트워크 요청 X

 

**캐시 지우는 방법 webView.clearCache(true)

 

layoutAlgorithm

HTML콘텐츠의 배치 방식을 설정하는 속성으로 Android10 (API29)부터 Deprecated됨

WebSettings.LayoutAlgorithm.NORMAL -> 기본 레이아웃 방식, 웹 브라우저처럼 표준대로 배치

WebSettings.LayoutAlgorithm.SINGLE_COLUMN -> 콘텐츠를 하나의 열로 정렬 (줄바꿈 포함)

WebSettings.LayoutAlgorithm.NARROW_COLUMNS -> 콘텐츠를 좁은 화면에 자동 맞춤

WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING -> 텍스트 크기를 자동으로 조정

 

pluginState

WebView에서 플러그인(Flash 등)을 사용할 수 있게 할지를 설정하는 속성이지만 Android 5.0 (API21)이상 부터는 플러그인 자체가 제거되어 동작하지 않음

 

mixedContentMode

혼합 컨텐츠를 어떻게 처리할지 설정하는 속성

WebSettings.MIXED_CONTENT_NEVER_ALLOW -> 혼합 콘텐츠 전부 차단 (default)

WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE -> 안전한 일부 콘텐츠만 허용(이미지) - 권장됨

WebSettings.MIXED_CONTENT_ALWAYS_ALLOW -> 모든 혼합 콘텐츠 허용(보안 취약)

**혼합 컨텐츠란https://로 열린 웹페이지에서 http://리소스를 불러오는 경우를 지칭한다. (http이미지, 스크립트, iframe 등을 사용하는 경우)

 

mediaPlaybackRequiresUserGesture

웹 뷰에서 오디오나 비디오같은 미디어가 자동으로 재생 될 수 있는지를 설정하는 속성 

true(default): 사용자가 클릭 등 제스처를 해야만 재생 가능 

false: 자동 재생 허용 (html에서 autoplay와 muted 속성을 같이 넣어줘야 자동재생됨 - 보안을 위해 대부분의 브라우저는 소리나는 미디어의 자동 재생을 막고있음) 

 

loadsImagesAutomatically

웹뷰에서 이미지를 자동으로 로드할지 여부를 묻는 속성 

true(default): 웹페이지에 포함된 이미지들을 자동으로 로드

false: 이미지 로드를 자동으로 하지 않음(텍스트만 보여짐)

안드로이드의 xml에서는 ConstraintLayout이랑 RecyclerView, LinearLayout만 잘 다룰줄 알면 간단한 뷰는 거의다 구현 할 수 있다.

 

ConstraintLayout은 뷰들간의 제약을 걸어 체인 형태로 묶어주어 간단하게 뷰의 위치를 제어할 수 있는 장점을 갖고있다.

오늘은 그 중에서 ChainStyle에 대해 학습해 보도록 하자.

 

자식 뷰들의 체인스타일을 수직으로 할지 수평으로 할지 먼저 정한다.

layout_constraintHorizontal_chainStyle	//체인 스타일을 수평으로 설정
layout_constraintVertical_chainStyle	//체인 스타일을 수직으로 설정

 

수직으로할지 수평으로 할지 정했으면 ChainStyle을 설정해주면 되는데 

ChainStyle에는 3가지가 있다.

1. packed (가운데로 합쳐짐)

2. spread (균등하게 배분)

3. spread_inside (양 끝 고정 후 남은 공간에 대해 균등하게 배분)

 

우선 imageView3개를 박스 형태로 뷰들간에 연결을 해주고 chainStyle을 적용하지 않은 상태에서 보자.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/box1"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:background="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/box2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/box2"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:background="#894753"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/box3"
            app:layout_constraintStart_toEndOf="@+id/box1"
            app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/box3"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:background="#385203"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/box2"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

ChainStyle이 적용 되지 않은 상태

chainStyle을 적용하지 않고 뷰들간에 동일한 체인을 걸어 화면의 정중앙에 동일한 간격을 갖고 배치된것을 볼 수 있다.


ChainStyle을 적용한 상태

왼쪽 순서대로 packed, spread, spread_inside로 설정을 했는데 chainStyle은 가장 왼쪽 뷰(Horizontal_chainStyle일 때) 또는 위쪽뷰(Vertical_chainStyle일 때)에 설정해주어야 지정된다.

 

packed (가운데로 합쳐짐)

체인이 연결된 가장 바깥쪽 끝 뷰를 기준으로 가운데로 뭉친 다음 뷰들 간의 간격을 지정해주는 방식이다. 

 

spread (균등하게 배분)

가장 바깥쪽 뷰를 기준으로 정렬한 후 뷰들 간의 사이가 동일하게 간격을 나누는 방식이다. 

 

spread_inside (양 끝 고정 후 남은 공간에 대해 균등하게 배분)

가장 바깥쪽 뷰를 부모뷰에 고정 시킨 다음 나머지 공간에 대해서 여백을 동일하게 나누는 방식이다.

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/box1"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:background="@color/black"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/box2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/box2"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:background="#894753"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/box3"
        app:layout_constraintStart_toEndOf="@+id/box1"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/box3"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:background="#385203"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/box2"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

+ Recent posts