(잡소리)
3년 9개월 차.. 여러가지 진짜 안좋은 상황으로 타의로 회사를 나오게 되었고 ^^ ,, 여태 한 번 빼고 다 타의로 회사를 나온건데 이력서가 엉망진창이 되어가고 있었고,, 그래서 서류상에 불리한 조건이 되었다. 서류 광탈도 광탈이고, 면접때도 꼭 물어보는 질문이 되었다..
"2년 이상 근속한 회사가 없으신데 왜 그러실까요?"
여러분들도 이력서 중에서는 꼭 오래 근속한 기록 하나는 만들어두기..
면접을 한 다섯번? 은 본거 같고. 원하는 회사에 합격 통보를 받아서 가게 되었다.
기술면접은 보통 이전 회사들에서 진행됐던 프로젝트들에 대한 질문 + 안드로이드에 대한 질문으로 이루어져있다.
그 사이에 기술면접 시에 받았던 질문들과 그에 대한 답(사실 면접때 쫄아서 답 못한게 더 많음)을 까먹기 전에 정리해보고자 한다. 생각나는 데로 쓸거라 두서는 없슴.
1. 안드로이드에 존재하는 네 가지 컴포넌트에 대해 설명해보세요.
1) 액티비티(Acitivity)
사용자와 상호작용을 하는 인터페이스이며, 실제로 사용자에게 보이는 화면을 의미합니다. 두 개 이상의 액티비티를 동시에 보여질 수는 없습니다.
한 개 이상의 뷰(View) 혹은 뷰그룹(ViewGroup)을 포함합니다.
라이프사이클 관련 매서드들을 재정의하며 원하는 기능을 구현할 수 있습니다.
인텐트(Intent)를 통해 다른 애플리케이션의 액티비티를 호출할 수 있습니다.
애플리케이션에는 반드시 하나 이상의 액티비티가 존재합니다.
액티비티 내에 프래그먼트를 추가하여 화면 분할이 가능합니다.
2) 서비스(Service)
사용자와 직접 소통하진 않지만 백그라운드에서 작업을 처리하는 용도로 사용합니다.
애플리케이션이 종료되어도 백그라운드에서 동작하는 컴포넌트입니다.
사용자의 눈으로 볼 수 있는 포그라운드 서비스와 눈에 보이지 않는 백그라운드 서비스로 나뉘며, 포그라운드 서비스는 반드시 알람을 표기해야합니다. 그렇다고 포그라운드 서비스가 상호작용만을 해야하는 것은 아니며, 사용자가 앱과 상호작용 하고 있지 않을때에도 실행은 계속 되고 있습니다.
네트워크 연동이 가능합니다.
액티비티와 서비스는 동일한 스레드 위에서 실행됩니다.
3) 브로드캐스트 리시버(BroadCast Receiver)
안드로이드 OS로부터 발생하는 각종 이벤트와 정보를 받아 핸들링하는 컴포넌트입니다.
안드로이드 디바이스의 상황에 대응하기 위해 사용됩니다. (ex. 재부팅, 네트워크 끊김, 배터리 부족, 문자수신 .. )
특수한 경우를 제외하고 UI를 가지지 않으며, 시스템 단에서 시작됩니다.
4) 콘텐츠 제공자(Content Provider)
콘텐츠 제공자는 데이터를 관리하고 애플리케이션에게 데이터를 제공하는데 사용되는 컴포넌트입니다.
외부 어플리케이션이 현재 실행중인 애플리케이션 내의 데이터에 선택적으로 접근할 수 있도록 도와줍니다.
데이터의 Read, Write 퍼미션이 있어야 접근할 수 있습니다.
2. Coroutine Dispatcher에는 네 가지가 존재하는데, 각각의 이름과 기능을 설명하세요.
1) Dispatcher.Main
UI와 상호작용 하는 작업을 실행하기 위해서 사용됩니다.
2) Dispatcher.IO
디스크 또는 네트워크 I/O 작업에 최적화 된 디스패쳐입니다.
3) Dispacher.Default
CPU Core만큼 돌릴수 있어 CPU를 많이 사용하는 작업을 실행하기 위한 디스패쳐입니다.
4) Dispacher.Unconfined
호출 시점의 스레드에서 사용되는 디스패쳐입니다. 호출 시점의 디스패쳐가 어떤 것인지 알 수 없는 케이스가 많아 이용에 조심해야만 합니다.
3. Coroutine을 쓰는 이유에 대해서 설명해보세요.
코루틴을 적용하면 장시간 작업으로 인항 메인스레드 블로킹 현상을 줄일 수 있으며, 비동기 작업 중 예외 발생에 따른 메모리 누수를 방지합니다. 또한 스레드를 직접적으로 변경하는 것이 아니라 교체에 비용이 적게 들기 때문에 가볍게 동작하며, CPU 상태에 따라 개발자가 자율적으로 처리할 수 있습니다. 또한 코틀린을 통해 비동기 처리 코드를 쉽고 가동석 있게 정리할 수 있습니다.
4. Coroutine Job의 역할을 설명해보세요.
코틀린 코루틴을 컨트롤하기 위한 것으로 Job을 통해 하나 이상의 코루틴을 제어할 수 있습니다.
job에는 상태가 존재하고(new, active, completing, canceling, completed, canceled), 그 상태에 따라서 코루틴을 제어하는 데 도움을 줄 수 있습니다.
5. Rest API 에 대해서 아는데로 설명해주세요.
Rest를 기반으로 만들어진 API입니다. 여기서 Rest란 HTTP URI를 통해 자원을 명시하고, HTTP Method를 통해 해당 자원의 CRUD(Create, Read, Update, Delete) Operation을 적용하는 것을 의미합니다.
따라서, REST API는 REST 규칙을 따라야 하기 때문에 다음과 같은 것을 포함압니다.
1) 자원(URL)
2) 행위(Verb) : HTTP Method(GET, POST, PUT, DELETE)
3) 표현 : Request가 들어오면 Server은 이에 대해 적합한 Response를 던져줘야 합니다. 주로 JSON이나 XML로 던져줍니다.
6. GET, POST, PUT, DELETE는 어떤 케이스에 쓰는 것이 적합합니까?
1) Get
주로 데이터를 읽거나 검색할 때 사용되는 메서드입니다.
데이터만 조회하는 것이기 때문에 Body값과 Content-type이 비어서 Request를 요청합니다. 조회할 데이터는 URL을 통해 파라메터를 받습니다.
만약 데이터 서치에 성공했다면 Body 값에 데이터 값을 저장하여 성공 응답을 보냅니다. 실패한다면 주로 404나 400에러를 리턴합니다.
Get은 캐싱이 가능하여 같은 데이터를 한 번 더 조회할 경우 저장한 값을 사용하여 조회하므로 조회 속도가 빨라지는 특성이 있습니다.
(프론트엔드에선 주로 페이지 로드나, 검색 기능 개발 시 사용하는 케이스가 많습니다.)
2) POST
주로 새로운 리소스를 생성할 때 사용됩니다. GET처럼 단순 데이터를 조회하는 방식이 아니기 때문에 같은 POST 요청을 했다고 해서 같은 리턴값을 보장하지 않습니다. 데이터를 생성하는 것이기 때문에 BODY 값과 Content-type을 작성하여 보내야 합니다. 데이터 생성에 성공한다면 Body 값에 데이터 값을 저장하여 성공 응답을 보냅니다.
(프론트엔드에선 주로 민감한 정보들을 보내야 할 때 사용하는 케이스가 많습니다. 예를 들면 로그인이라던지, 회원 정보 변경이라던지.)
3) PUT
데이터를 생성하고 업데이트 하는데에 사용합니다. 수정된 값을 보내는 것이기 때문에 같은 값으로 PUT을 시도한다면 같은 리턴값을 보장합니다. 이도 마찬가지로 데이터를 수정하는 것이라 Body 값과 Content-type을 보내줘야 합니다. 데이터 수정에 성공한다면 Body 값에 데이터 값을 저장하여 성공 응답을 보냅니다.
4) DELETE
데이터를 삭제하는데 사용됩니다. GET처럼 URL에 데이터의 파라메터를 보냅니다. 따라서, Body와 Content-Type을 보낼 필요가 없습니다. 데이터 삭제에 성공한다면 BODY 값에 성공응답만 보내게 됩니다.
7. Kotlin에서 사용하는 null safety 에 대해 설명하고, nullable 한 변수를 not-nullable 하게 방법을 설명해보세요.
Java등의 언어에서 가장 크게 발생하는 오류가 null point exception인데, 이 위험성을 없애기 위해서 나온 것이 null safety입니다.
코틀린에서는 ?를 통해 null 이 들어올 수 있는 변수와 null이 될 수 없는 변수를 구분합니다.
?이 붙었다면 null을 허용하는 변수, 없다면 허용하지 않는 변수입니다.
그리고 null을 허용하는 변수에 접근하는 방법은 다음과 같습니다.
1) ?:(앨비스 연산자) 를 통하여 값이 null일 경우에 default 값을 정해주므로서 접근 가능
2) 변수?.let을 통해 해당 변수가 null이 아닐 경우에 넘어오는 람다함수 내부의 값을 통해 접근 가능
3) !!(assert code)를 통해 강제적으로 이것이 null이 아님을 선언하기
4) if-else 구문을 통해 null인지 아닌지 구분하기.
8. Coroutine runBlocking 에 대해 설명해주세요.
runBlocking은 현재 스레드가 종료될 때 까지 해당 함수를 블로킹하는 매서드입니다. 이는 자바에서 동기처리 시 블로킹 하는 것과 같은 방식입니다. 비동기함수를 사용하다 보면 반드시 해당 스레드를 잠깐 블로킹 해야하는 상황이 오는데, 일반적인 suspend 함수는 스레드를 블로킹 하지 않기 때문에 애플리케이션이 죽을 수 있는 문제가 발생할 수 있습니다. 그래서 runBlocking을 이용해서 해당 스레드를 잠깐 블로킹 해두는 작업을 진행합니다.
9. Kotlin에서 사용하는 영역함수에 대해 아는데로 말해주세요.
1) run
컨택스트 개체는 람다 함수 내에서 수신자 this로 사용할 수 있습니다.
반환 값은 람다의 결과값입니다.
2) with
컨택스트 개체는 람다함수 내에서 수신자 this로 사용할 수 있습니다.
반환 값은 람다의 결과값입니다.
여기서 run과 다른점은 with의 매개변수의 첫번째 인자로 문맥 식을 던져야 한다는 점입니다. 그래서 with는 객체의 호출과 함께 이를 확장해야하느 경우 사용합니다.
(예시)
fun main() {
val call1 = Person().run {
age = 30
name = "Mary"
println("name : $name, age : $age")
}
val call2 = with(Person(30, "Mary")) {
println("name : $name, age : $age")
}
}
class Person {
var age : Int = 0
var name : String = ""
}
3) let
컨텍스트 개체는 인수 it입니다.
반환 값은 람다 결과입니다.
주로 null check 가 필요한 시기에 사용합니다.
val a : Int? = getNumber()
a?.let {
println(it + 10)
}
4) apply
컨택스트 개체는 수신자 this 입니다.
반환 값은 개체 자신입니다.
객체의 상태를 지정하는데에 사용합니다
fun main() {
Person().apply {
age = 30
name = "Mary"
}
}
class Person {
var age : Int = 0
var name : String = ""
}
5) also
컨택스트 개체는 인자 it 입니다.
반환 값은 개체 자신입니다.
객체의 상태를 지정하는데에 사용합니다. apply 와 다른점은 람다 함수 내에서 인자로 받느냐, 수신자로 받느냐의 차이입니다.
fun main() {
Person().also {
it.age = 30
it.name = "Mary"
}
}
class Person {
var age : Int = 0
var name : String = ""
}
10. Kotlin Data Class 에 대해 설명해주세요.
Data Class 는 toString() , hashCode(), equals(), copy() 메소드를 자동으로 만들어줍니다. 또 getter&setter 를 선언할 필요가 없다는 점도 장점입니다.
특징은 다음과 같습니다.
1) 상속이 불가합니다.
2) val 또는 var로 선언해야합니다.
3) abstract, open, sealed, inner을 붙일 수 없습니다.
4) 1개 이상의 프로퍼티를 가져야 합니다.
따라서 데이터를 저장하기에 용이하며 보일러 플레이트 코드를 줄일 수 있습니다,.
11. Kotlin Sealed Class 에 대해 설명해주세요.
Sealed Class 는 자신이 추상클래스가 되어 상속을 제한하는 클래스이다. enum과 다르게 스스로 상속을 지원한다. 즉, 컴파일러에게 sealed class 내부의 자식 이외에 내게 다른 자식 클래스는 없다고 알려주는 클래스이다. 따라서, when 을 사용할때 확실히 내부에 자식이 어떤 것인지 알 수 있기 때문에 else를 사용할 필요도 사라진다.
12. Kotlin inner Class에 대해 설명해주세요.
자바에선 기본적으로 클래스 내에 클래스를 만들면 내부 클래스로 인정 받지만, 코틀린에서는 클래스 내에 클래스를 만든다고 해서 중첩 클래스(nested)가 되지, 내부 클래스(inner)가 되진 않는다. 즉, inner 클래스라고 명확하게 설명해주지 않으면 클래스 외부의 값을 참조할 수 없다.
ex)
// nested example
class NestedExample {
private val number = 1
class Nested {
// 외부의 number을 참조할 수 없음
fun nestedTest() = 2
}
}
val nestedExample = NestedExample.Nested().nestedTest() // 2
// inner example
class InnerExample {
private val number = 1
class Inner {
fun innerTest() = number
}
}
val innerExample = InnerExample.Inner().innerTest() // 1
13. DI(의존성 주입) 에 대해 설명해주세요.
구성 요소간의 코드 내부가 아닌 외부에서 값을 넘겨주는 디자인 패턴을 말한다.
만일 클래스 내부에서 다른 클래스를 객체로 가진다면 서로의 의존성이 생기면서 다른 클래스 수정 시 참조하고 있는 모든 클래스를 수정해줘야 하는 불상사가 생긴다. 그러나 외부에서 생성자를 만들어 클래스 주입을 시도하면 객채간 의존성이 줄어든다.
따라서, 재사용성이 높아지고 유지보수가 용이해지며, 결합도가 낮아져 확장성이 높아진다.
ex)
// 의존성이 있는 케이스
class Market {
private val fruit = Fruit()
fun sell() {
fruit.sell()
}
}
fun buy() {
val customer = Market()
customer.sell()
}
// 의존성 주입 케이스
class Market(private val fruit : Fruit) {
fun sell() {
fruit.sell()
}
}
fun buy() {
val customer = Market(Fruit())
customer.sell()
}
14. 앱의 유지보수를 위해서는 객체의 의존성을 낮추는 일이 중요한데요, 이를 위해 할 수 있는 방법을 설명해주세요.
1) 의존성 주입
2) 모듈화와 인터페이스 추상화
3) 클린 아키텍쳐
15. DP와 SP의 차이를 설명해주세요.
DP는 UI 레이아웃을 정의할 때 레이아웃 치수나 위치를 지정하기 위해 사용하는 단위입니다. 화면의 크기가 달라도 동일한 비율로 보여주기 위해 안드로이드에서 정의한 단위입니다.
SP는 레이아웃을 정의할때 텍스트의 크기를 지정하기 위해 사용하는 단위입니다.
따라서, 둘의 차이는 고정값이냐 고정하지 않느냐, 의 문제입니다. dp는 화면에 크기에 구애받지 않고 고정됩니다. 하지만 sp는 시스템 사이즈에 따라 변화하는 값입니다.
16. 기기마다 해상도가 다를 수 있는데, 각 기기의 해상도에 따라 앱을 개발할 수 있는 방법을 제시해주세요.
(저는 실무에서 해상도마다 뷰를 짠 적이 있기 때문에 이 경험을 바탕으로 얘기했습니다.)
17. Coroutine에서 CoroutineScope와 GlobalScope에 대해 설명해주세요.
CoroutineScope는 개발자가 작업을 추적하고 수명을 제한할 수 있으며, Job을 통해 원할때 마다 작업을 취소할 수 있습니다. 따라서 CPU와 메모리를 효율적으로 관리할 수 있습니다. 그러나 GlobalScope의 경우 애플리케이션의 가장 최고 수준의 스코프입니다. 따라서 앱의 라이프사이클과 함께 움직이기 때문에 앱이 죽을때 까지 작업을 유지합니다. 그런 이유로 파라메터로 CoroutineContext를 가지지 않으며, 기본적으로 Dispatcher.Default를 가집니다.
18. 안드로이드 라이프 사이클에 대해 설명해주세요. 그리고, 이것을 프로젝트에 적용한 사례에 대해서 설명해주세요.
안드로이드는 적어도 하나의 액티비티를 가져야하기 때문에 보통 액티비티 라이프 사이클을 의미하기도 합니다.
액티비티 라이프 사이클은 다음과 같습니다.
onCreate() : 화면 레이아웃 정의, 뷰 생성. 가장 먼저 호출 되며, 단 한번만 호출됨.
onStart() : 화면에 진입할때마다 호출
onResume() : 화면에 보여질 때 호출. 사용자에게 포커스 된 상태.
onPause() : 화면에 보여지지 않을 때 호출. 사용자에게 포커스 아웃 된 상태.
onStop() : 액티비티가 완전히 가려질 때 호출(다른 액티비티 이동, 홈 키로 앱 밖으로 이동). 이 상태로 재진입시 onRestart -> onStart 호출
onDestroy() : 액티비티가 완전히 종료될 시 호출.
(저같은 경우는 스트리밍이나 웹소켓을 실무에 쓴 적이 있어서 이를 바탕으로 말씀드렸습니다.)
19. 안드로이드 뷰를 나타낼때 Activity와 Fragment를 많이 사용하는데, 이 두 개의 차이점을 설명해주세요.
프래그먼트는 모듈화 할 수 있으면 재사용 가능하며, 여러 액티비티에서 나타나는 UI 구성 요소에 이상적입니다. 또한 프래그먼트는 복잡한 UI에 많은 유연성을 제공하며, 대형 레이아웃에서 한 액티비티 위에 여러 프래그먼트를 표기할 수 있습니다.
그러나, 액티비티는 여러 프래그먼트를 호스트할 수 있습니다. 또한, 프래그먼트의 생명주기는 어디까지나 액티비티에 종속되어있습니다.
따라서, 액티비티의 경우 독립적인 성격의 작업에 이용되며, 프래그먼트의 경우 재사용 가능하고 동적인 UI를 만드는데에 사용됩니다.
20. MVVM 패턴은 무엇을 말하는 것이며, 왜 사용하는지 알려주세요. 구체적으로 어떻게 사용했는지도 알려주세요.
MVVM 패턴은 모델, 뷰, 뷰모델을 분리해 뷰와 모델간의 의존성을 줄여주는 디자인 패턴입니다. 여기서 뷰는 단순히 뷰만 그리고, 뷰모델은 데이터의 업데이트만을 실시합니다. 뷰는 뷰모델에서 데이터를 옵저빙하여 사용하면 됩니다.
장점으로는 뷰와 모델의 관심사를 분리할 수 있고, 뷰가 재생성되어도 뷰모델에 데이터가 저장되어있기 때문에 데이터를 유지할 수 있다는 장점이 있습니다. 또한 뷰모델은 뷰가 onDestroy 될 때 함께 제거되어(좀 더 자세히 말하면 뷰가 다시 살아날 때의 재생성 비용을 줄이기 위해 한동안은 viewModelScope에 남아있습니다.) 효율적인 메모리 관리를 돕습니다.
20. MVP 패턴은 무엇을 말하는 것이며, 왜 사용하는지 알려주세요.
21. Parcelable을 사용해본 적이 있나요? 어떤 것인지 설명해주세요.
22. 사용해본 오픈소스 라이브러리들을 생각나는데로 얘기해주세요.
23. API 요청시 네트워크 오류가 났을 때, 어떻게 처리하면 좋을까요?
24. Compose를 사용해보신 적이 있으세요? 써본 후기를 알려주세요.
25. CI/CD에 대한 경험이 있으세요?
26. 안드로이드 웹뷰를 사용했을 때의 문제점에 대해 설명해주세요.
(이 때 예시를 주셨는데, 안드로이드 앱 상에서는 로그인 된 것처럼 취급되지만, 웹에서는 로그인 되지 않은 것으로 처리될 수도 있음. 이 때의 처리 방식에 대해서 질문이었음.)
27. Object 에 대해서 설명해주세요.
28. 코틀린 제네릭에 대해 설명해주세요.
'기타' 카테고리의 다른 글
슬럼프? (0) | 2022.09.19 |
---|---|
test (0) | 2021.02.25 |
자주 쓰는 안드로이드 Gradle (0) | 2020.07.15 |
안드로이드 개발에 유용한 사이트 (0) | 2020.07.08 |