Week 5 - 역량강화 마지막 😭


이번 주차는 역량강화 마지막 주차이다.. 지금까지 진행해왔던 교양강화, 전공강화, 역량강화 팀과 함께할 수 있는 마지막 시간이다. 이런 부분이 시원섭섭하지만, 빡빡했던 스케줄을 가지고 있었던 역량강화 기간을 끝내고나니 후련하다는 생각도 들고 해방감(?)도 들게 되었다.

드디어 다음 주 부터는 팀 프로젝트 위주로 시간을 잘 활용할 수 있을 것 같고, 개발을 할 생각을 하니 한 편으로는 좀 걱정이 되긴 하지만, 진짜 드디어 내가 할 일을 맘 놓고 할 수 있다는 생각도 들고, 한 편으로는 설레기도(?) 한다 !!

물론 앞으로 전공 역량강화는 팀 프로젝트를 진행하면서 필요하거나 부족한 부분에 대해서는 자율적으로 학습하거나 강의를 듣고 보완을 할 수 있다고 생각한다.

📝 교양강화

[애자일 마스터]


이번, 애자일 마스터 발표 범위는 남은 범위인 13장~15장이다.

13. 리팩터링 : 기술적 부채 갚기

먼저, 13장에서는 리팩터링이 무엇인지, 기술적 부채를 갚아나감으로써 어떻게 소프트웨어를 빠르고 유연하게 만들 수 있는 지에 대한 내용을 다룬다.

우리는 먼저, 기술적 부채란 무엇인지 살펴보고자 한다.

기술적 부채

  • 생산성 또는 일정이라는 명목하에 무질서하게 코드를 배치하고 복사 붙이기를 함으로써 생기는 부채.

  • 오랫동안 쌓여온 이러한 부채는 코드의 작동을 파악하기 어렵게 함.

  • 이러한 결함들로 인해서 장애가 발생할 수 있고, 해결할 수 없음.

  • ex) 스파게티 코드, 복사 붙이기 등등...

이와 같은 기술적 부채는 리팩터링을 진행함으로써 해결해나갈 수 있다.


Refactoring은 말 그대로, 코드의 구조를 재정의한다는 것이다.

정확한 리팩터링의 정의에 대해서 살펴보자.

리팩터링

  • 겉으로는 큰 변화가 없지만, 설계(코드의 구조)를 점진적으로 향상시켜 코드의 이해력을 높이는 방법

  • 리팩터링의 작업을 프로젝트 진행 중 꾸준히 했을 때, 작업 속도가 빨라질 수 있고, 유연하게 요구사항에 대응할 수 있고, 유지 보수성이 향상될 수 있다.

  • 리팩터링의 종류

    • 변수나 메서드 이름 교체
    • 코드가 중복된다면 메서드로 추출.

리팩터링 팁

  • 리팩터링의 규모가 클 때
    • 사용자 스토리와 같이 취급하자.
    • 추정치와 우선순위를 매기고, 리팩터링에 드는 비용을 예측해보자.

  • 리팩터링의 결과로 돌아오는 보상이 적다면 ?
    • 프로젝트가 거의 끝나가는 단계라면, 진행하지 말자.
    • 이 리팩터링을 고객에게 가치를 전달하는 과정과 같이 점차적으로 늘려갈 수 있는지를 확인하자.

14. 테스트 주도 개발

14장에서는 테스트 주도 개발(TDD)이 무엇인지에 대해서 살펴본다.

먼저, TDD가 무엇인 지 살펴보자.

매우 짧은 개발 주기를 사용하여 소프트웨어의 설계를 점진적으로 향상시키는 기법

매우 짧은 개발 주기를 가짐으로써, 그 기능에 대한 테스트를 통해 고객이 진짜 필요로 하는 것을 개발하였는 지, 그 기능이 잘 작동하는 지를 테스트하면서 개발을 하면 설계를 향상시켜나갈 수 있다는 얘기이다.

TDD 작동 원리

  • RED - 실패하는 단위 테스트를 작성한다.
  • GREEN - 테스트를 통과시키는 것이 주 목적이다. 실행 흐름을 충분히 이해했다면, 최소한의 분량으로 코드를 추가한다.
  • REFACTOR - 설계를 개선하기 위해 코드들을 검토하는 단계이다. 테스트의 통과를 유지하기 위해 리팩터링을 진행한다.

TDD 규칙
작동원리와 유사하다.

  1. 실패하는 테스트를 먼저 작성하고, 작성하기 전까지는 코드를 추가하지 않는다.
    ( 테스트를 먼저 작성하기 위해서는 어떤 가치를 전달하기 위한 것인지 파악하자. )

  2. 문제가 생길 가능성이 있는 것이라면 반드시 테스트릍 하자.

TDD 이점

  1. 필요한 것만 개발할 수 있고, 동시에 잘 작동하는지 테스트 할 수 있다.

  2. 단순한 설계를 만들 수 있다.

  3. 짧고 품질이 좋은 코드를 유지할 수 있다.

  4. 코드에 대한 집단적 소유 의식을 통해 팀원 개인의 부담감을 덜 수 있다.


15. 지속적인 통합 : 출시 준비

15장에서는 지속적인 통합(CI, Continuous Integration)을 다루는 장이다.

먼저, 지속적인 통합이란 무엇일까 ?

지속적으로 소프트웨어를 변경하고, 변경사항들을 계속해서 통합하는 활동

소프트웨어 출시 준비를 너무 특별하지 않고 자연스럽게 하기 위해서 우리는 코드를 지속적으로 통합하고, 항상 출시에 대비해야 한다.

출시를 준비한다는 것은 소프트웨어를 개발, 통합, 배치하는 과정까지를 말한다.

지속적인 통합은 어떻게 하는가 ?

  • 소스코드 저장소를 사용한다. Git, Subversion

  • 체크인 프로세스를 수립한다.

    • 저장소에서 최신 소스코드를 받는다.
    • 코드를 수정한다.
    • 잘 작동하는지 테스트를 실행한다.
    • 혹시나 바뀐 코드가 있는지 업데이트를 확인한다.
    • 코드가 충돌되는 부분이 있었거나, 바뀐 코드가 합쳐졌을 때, 기능이 잘 작동하는지 다시 한번 테스트를 진행한다.
    • 체크인한다.
  • 자동화된 빌드를 사용한다.
    (자동화된 빌드는 코드를 컴파일, 테스트 실행을 모두 자동화하여 정기적으로 처리하며 사람의 실수를 줄여준다.)

  • 작은 단위로 나누어서 작업을 한다.
    ( 말 그대로 지속적인 통합 과정을 위해서는 작은 단위일수록 통합하기 유리하다. )


📝 전공강화

코루틴

코루틴이란 ?

  • 코루틴(Coroutine)은, 서로 협력하는 루틴을 말한다. Co(Cooperative) + Routine

  • 일시 중단이 가능한 작업 객체이다.

  • 스레드의 생성 비용을 줄여 메모리를 절약할 수 있다.

  • 경량 스레드이며, 동시성을 보장한다.

코루틴 Scope & Builder

  • 코루틴 Builder 의 종류로는 코루틴 스코프의 확장함수인launchasync 등이 있다.

  • launch의 경우, 코루틴을 시작하고, 작업의 상태를 추적하고 변경할 수 있는 Job 객체를 반환한다. (결과값이 아니다)

import kotlinx.coroutines.*
fun main() = runBlocking {
    // job은 생성과 동시에 실행된다.
    // 따라서 job은 필요한 위치에 생성을 해야하고, 그 위치에서 바로 실행을 시켜야하므로 유연성이 떨어진다.
    val job = GlobalScope.launch {
        delay(3000L)
        println("World!")
    }

    println("Hello")
    job.join()  //  job 이 속한 coroutine 이 종료될 때까지 기다린다.
}

join()메서드를 사용해서, 현재 스레드를 일시중단시키며, 작업이 마치기까지 기다린다.
현재 스레드에서 자식 코루틴으로 실행하려면 그냥 launch{ } 로 사용해도 된다.

  • async의 경우, 결과값이 필요한 경우에 사용하며, 람다의 마지막 줄 값이 결과값이 된다.
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {

    val job3 = CoroutineScope(Dispatchers.Default).async {

        kotlin.io.println("작업3 수행중")
        val result = (1..10).sortedByDescending { it }
        kotlin.io.println("작업3 수행완료")
        result
    }

    val job1 = launch {

        kotlin.io.println("작업1 수행")

        kotlin.io.println("작업1 일시중단")
        val job3Result = job3.await()       // 작업3의 결과가 필요하여 작업1을 수행하는 코루틴이 일시중단 되었다.

        kotlin.io.println("작업1 재개")
        job3Result.forEach { print("$it ") }
        println()
        kotlin.io.println("작업1 수행완료")
    }

    val job2 = launch {
        delay(1L)
        kotlin.io.println("작업2 수행")
        kotlin.io.println("작업2 수행완료")
    }

}

launchasync는 다음과 같이 사용하면 된다.
다음과 같이 asyncawait() 메서드를 통해, 현재 스레드를 일시중단 시키며, 결과값을 가져온다.

  • 코루틴 Scope는 코루틴이 실행되는 범위이며, LifeCycle에 따라 원하는 scope를 생성하여 원하는 실행 범위를 지정할 수 있다.
    • 사용자 지정 스코프 - CoroutineScope
    • 앱 전체 생명주기 스코프 - GlobalScope

코루틴 문맥(Coroutine Context)

  • 코루틴은 항상 코틀린 라이브러리의 CoroutineContext를 상속받는 문맥에서 동작한다.

  • 코루틴에서 사용할 수 있는 여러가지 데이터 정보가 들어있다.

  • 코루틴 이름, 디스패처, 작업 상세사항, 예외 핸들러 등

Dispatcher

  • 코루틴은 특정 문맥에서 실행된다고 하였는데, 디스패처는 코루틴이 어떤 문맥에서 실행될지 스레드를 연관시켜주는 역할을 한다.
  • Dispatcher의 종류
    • Dispatchers.Main - Main 스레드에서 동작하며, Android 에서는 UI 스레드 역할을 하므로, UI를 업데이트하는 역할을 주로 한다.
    • Dispatchers.Unconfined - 지정되지 않은 Dispatcher로 문맥이 지정되지 않고, 호출된 문맥에서 실행된다.
    • Dispatchers.IO - 네트워크 통신, 입출력 작업 등에 주로 사용.
    • Dispatchers.Default - CPU 연산을 많이 하는 작업에 주로 사용.

[ 사용 예시 ]

CoroutineScope(Dispatchers.Main).launch{
	updateButton() // 필요한 작업 수행
}

예제)

fun main() = runBlocking { // main 스레드에서 동작

		// 인자를 넘겨주지 않은 경우: 상위 코루틴의 context
    launch {
        println("main runBlocking       : 나는 ${Thread.currentThread().name} 에서 돌아")
    }

    // 별도 지정 안함: 이를 호출한 스레드에서 동작
		launch(Dispatchers.Unconfined) {
        println("Unconfined             : 나는 ${Thread.currentThread().name} 에서 돌아")
    }
    
		// 공유된 background 스레드 풀 사용
		launch(Dispatchers.Default) {
        println("Default                : 나는 ${Thread.currentThread().name} 에서 돌아")
    }

		// 직접 만든 스레드 사용
    launch(newSingleThreadContext("NewSingle_Thread")) {  // 새로운 스레드 생성
        println("newSingleThreadContext : 나는 ${Thread.currentThread().name} 에서 돌아")
    }
    
    println("runBlocking            : 나는 ${Thread.currentThread().name} 에서 돌아")
}

//실행 결과
Unconfined             : 나는 main @coroutine#3 에서 돌아
runBlocking            : 나는 main @coroutine#1 에서 돌아
Default                : 나는 DefaultDispatcher-worker-1 @coroutine#4 에서 돌아
newSingleThreadContext : 나는 NewSingle_Thread @coroutine#5 에서 돌아
main runBlocking       : 나는 main @coroutine#2 에서 돌아

코루틴 예외처리

  • 자식 코루틴에서 exception이 발생하게 되면, exception은 부모까지 전파된다.

  • 이러한 경우 CoroutineContext 문맥에 CoroutineExceptionHandler 를 추가해서 예외를 처리할 수 있다.

    import kotlinx.coroutines.*
    
    suspend fun main(){
    // handler는 CoroutineExceptionHandler을 구현하며
    // exception이 왔을 때 받은 exception을 출력해줍니다.
        val handler = CoroutineExceptionHandler{_, exception ->
            println("CoroutineExceptionHandler : $exception")
        }
    
        val job = CoroutineScope(Dispatchers.IO).launch(handler){
            throw IllegalArgumentException()
        }
        delay(1000)
    
    }
    import kotlinx.coroutines.*
    
    suspend fun main(){
        val handler = CoroutineExceptionHandler{_, exception ->
            println("CoroutineExceptionHandler : $exception")
    
            when(exception){
            is IllegalArgumentException -> println("More Argument Needed To Process Job")
            is InterruptedException -> println("Job Interrupted")
            }
        }
    
        val job1 = CoroutineScope(Dispatchers.IO).launch(handler){
            throw IllegalArgumentException()
        }
    
        val job2 = CoroutineScope(Dispatchers.IO).launch(handler){
            throw InterruptedException()
        }
    
        delay(1000)
    
    }
  • launch의 경우, 바로 실행하기 때문에, 전파가 바로 되지만, asyncawait()를 호출하기 전까지 전파하지 않는 성질이 있다.

    • async 빌더는 왜 전파되지 않을까 ?
      • SupervisorJob을 사용한다.
        SupervisorJob을 exception으로 취소 될 자식에게 설정해두면, 그 자식이 취소되었을 때, exception이 전파가 되지 않는다.

      • 여러 자식을 SupervisorJob 으로 설정하려면 supervisorScope{}안에 자식 코루틴들을 실행하면 된다.

디자인 회의


디자이너님께서 저번 주말 작업을 하셔서 이번 주차에 프로젝트 앱 아이콘과, 메인 기능을 하는 화면들에 대한 디자인 레이아웃을 받았다.
처음에 아이콘을 봤을 때는 괜찮았는데, 팀 프로젝트 회의에서 한 팀원 분께서 아이콘에 대해 수정을 하는 것이 어떠냐는 의견을 제시하시고 논의 끝에 아이콘을 바꾸기로 했다.

그런데 아이콘 디자인을 바꾸는 부분에 있어서, 아이콘이 나왔을 때, 즉각적으로 피드백을 하였으면 좋았을텐데라는 아쉬운 점이 있다. 이미 디자이너님께 마음에 든다고 말을 한 뒤여서 앞 뒤가 말이 달라져버린 상황이 되어버린 것 같았다 ㅠㅠ

앞으로는 팀원 간 정기적인 회의도 진행하고, 이러한 산출물들에 있어서는 바로 즉각적으로 회의를 통해 결정을 하기로 하였다.

개발자 멘토링


어제, 금요일에는 안드로이드 개발자 멘토님과 함께 멘토링하는 시간을 가졌다.

다음 주 부터 진행하게 될 팀 프로젝트를 진행하는 데에 있어 기술적인 부분이나 앞으로의 취업에 있어서 많은 정보를 얻을 수 있었고, 맛있는 점심식사와 유쾌하고 건설적인 시간을 가질 수 있었던 것 같다.

첫 만남이라 어색했던 부분이 있어서, 매끄럽게 진행되지는 못했지만 다음 멘토링 시간에는 질문도 많이하고 정보를 많이 얻어볼 예정이다 !!!!
이번 멘토링때는 질문을 많이 준비하지 못해서, 프로젝트의 기술적인 부분에 대한 답변만 기억에 남는다 ㅠㅠ

마무리


이번 주차까지 해서 역량강화 기간이 끝이났다.. 이번 역량강화 기간을 통해서 앞으로 안드로이드 프로젝트를 진행하는데에 있어서 부족한 코틀린, 코루틴 지식들을 보완할 수 있었고, 교양강화를 통해서 애자일 방법론에 대해서 공부하고 앞으로 프로젝트에 어떻게 적용할 수 있는지, 프로젝트에 있어서 사고적인 부분이 애자일적으로 된 것 같다. ㅎㅎ 그리고 나머지 팀이 진행한 읽기 좋은 코드 , SQL 첫걸음 등을 통해서 코드를 클린하게 작성하는 방법과 정보처리기사 자격증을 취득한 이 후, 손 대지도 않은 SQL에 대해서 공부할 수 있던 기간이었다.

물론, 역량강화 기간이 끝났다고해서 본인이 역량강화가 필요 없다는 것은 아니라고 생각한다. 앞으로 프로젝트를 진행함에 있어 부족한 부분, 필요한 부분들은 당연히 찾아서 공부해야하고, 프로젝트 협업 부분에 있어서 어려움이나 갈등이 발생할 때, 이번에 배웠던 교양강화 부분을 생각하고 참고하여 해결할 수 있다고 생각한다. 부족하다면 교양도 더 공부할 수 있으면 좋을 것 같다.


유데미 바로가기: https://bit.ly/3SFlXDy

유데미 STARTERS 취업 부트캠프 공식 블로그 보러가기: https://blog.naver.com/udemy-wjtb

💡 본 후기는 유데미-웅진씽크빅 취업 부트캠프 2기 - 프론트엔드&백엔드 과정 학습 일지 리뷰로 작성되었습니다.

profile
Yoon's Dev Blog

1개의 댓글

comment-user-thumbnail
2022년 11월 17일

👍

답글 달기