월별 글 목록: 2022년 5월월

창파도서관 출입인증 AppleWallet Pass 제작 上편

방갑습니다 여러분 오랜만이에요

저는 흑우처럼 라이센스를 다시 샀습니다

6개월 풀할부로요

그러다 학교에서 공지가 떴습니다.

도서관 출입하려면 전자출결 들어가서 QR을 찍고 들어가라네요

실물 학생증 RF 태깅해서 들어가게 해주면 되지 굳이 저딴식으로 에휴;;;;

QR 생성 방식은 간단했습니다. 걍 학번 QR로 만든거였음;;;

그래서 Apple Pass로 만들어서 넣어다니려구요

결론은 만들었습니다. 이렇게요

도서관 좌표도 넣어서 도서관 앞에 가면 뜹니다. 학과랑 학번 이름까지 뜨게 해놓음

나 경영학과임 암튼 그럼ㅋㅋ

다들 게이트 앞에서 폰 만지작 거릴 때 바로 들어가면 기분 좋음 ㅋㅋ

코드 관련해서는 속편에 이어서 하겠습니다 그럼 이만

docker swarm

docker 는 작은 단위로 앱을 배포할 수 있게 해주는 서비스 입니다.

이미지를 만들어놓고 실행하는 상태를 컨테이너라고 하는데요. 분산 처리 목적으로 여러 서버에 컨테이너를 올리려면 상당히 번거롭습니다.

그래서 컨테이너를 자동으로 배치해주고 다양한 통합 기능을 제공하는 docker swarm 에 대해 간단하게 소개 드리겠습니다.

컨테이너를 자동으로 배치해주고 다양한 통합 기능을 제공하는걸 컨테이너 오케스트레이션 이라고 합니다. 직접 여러 서버에 접속해서 같은 명령어를 입력하는 수고를 자동화해준다고 보시면 되겠습니다.

swarm 모드에는 manager 와 worker 가 있습니다. 매니저는 매니저 노드와 worker 노드에 컨테이너를 분산 하여 실행시킵니다.

그림에서 나오는 것 처럼 고 가용성을 위해 매니저 노드는 최소 3개를 설정해야 합니다. 이렇게 설정하면 1개 노드가 죽어도 네트워크에 액세스 할 수 있습니다.

또 하나의 swarm 특징이 overlay 네트워크인데요, 어느 노드에 접속해도 동일한 네트워크에 접속한 것 처럼 가상의 망을 구축해 줍니다.

제2회 DU-IF 창업아이디어 경진대회 준비

대구대학교 교내 경진대회로 제2회 창업아이디어 경진대회 공고가 올라왔다. 생애최초청년예비창업에서 만난 사람들과 같이 나가기로 하였다.

준비한건 없지만 서류평가에 붙어서 다음주(30일) 발표하러 가야한다. 리번에도 상 타와야지

Kotlin 안드로이드 앱 프로그래밍 (10)

보조 생성자
보조 생성자는 클래스의 본문에 constructor 키워드로 선언하는 함수, 클래스 본문에 선언하므로 여러 개를 추가할 수 있습니다.
다음 코드에서는 매개변수를 다르게 구성한 보조 생성자 2개를 선언했습니다. 보조 생성자도 생성자 이므로 객체를 생성할 때 자동으로 호출됩니다. 그리고 보조 생성자는 클래스 본문에 선언하므로 생성자 본문을 중괄호 {}로 묶어서 객체 생성과 동시에 실행할 영역을 지정할 수 있습니다.

보조 생성자 선언

class User{
   constructor(name: String){
      println("constructor(name: String) call...")
   }
   constructor(name: String, count: Int){
      println("constructor(name: String, count: Int) call...")
   }
}
fun main(){
   val user1 = User("kkang")
   val user2 = User("kkang", 10)
}

>> 실행결과
constructor(name: String) call...
constructor(name: String, count: Int) call...

보조 생성자에 주 생성자 연결
코틀린의 생성자는 주 생성자와 보조 생성자로 나뉩니다. 클래스를 선언할 때 둘 중 하나만 선언하면 문제가 없지만, 만약 주 생성자와 보조 생성자를 모두 선언한다면 반드시 생성자끼리 연결해 주어야 합니다.

주 생성자와 보조 생성자 선언 시 오류

class User(name: String){
   constructor(name: String, count: Int){ // 오류
---
   }
}

클래스에 주 생성자와 보조 생성자를 모두 선언하였습니다. 그런데 위의 코드는 오류가 발생합니다. 주 생성자가 없다면 보조 생성자를 선언하는 데 문제가 없지만 주 생성자가 있으므로 보조 생성자에서 주 생성자를 호출해 주어야 합니다.
보조 생성자는 객체를 생성할 때 호출되며, 이때 클래스 내에 주 생성자가 있다면 this() 구문을 이용해 주 생성자를 호출해야 합니다.

보조 생성자에서 주 생성자 호출

class User(name: String){
   constructor(name: String, count: Int): this(name){ // 성공
---
   }
}
fun main(){
   val user = User("kkang", 10)
}

보조 생성자 선언부에 this(name)만 추가한 코드입니다. 이렇게 하면 보조 생성자로 객체를 생성할 때 주 생성자가 함꼐 호출됩니다.
만약 주 생성자가 있는 상태에서 보조 생성자를 여러 개 선언한다면 보조 생성자에서 this() 로 다른 보조 생성자를 호출할 수도 있습니다. 그런데 이때에도 보조 생성자로 객체를 생성한다면 어떤 식으로든 주 생성자가 호출되게 해야 합니다.

보조 생성자가 여럿일 때 생성자 연결

class User(name: String){
   constructor(name: String, count: Int): this(name){
---
   }
   constructor(name: String, count: Int, email: String): this(name, count) {
---
   }
}
fun main(){
   val user = User("kkang", 10, "a@a.com")
}

같은 모양으로 묶음

클래스를 재사용하는 상속 – 상속과 생성자
클래스를 선언할 때 다른 클래스를 참조해서 선언하는 것을 상속 이라고 합니다. 코틀린에서 어떤 클래스를 상속받으려면 선언부에 콜론: 과 함께 상속받을 클래스 이름을 입력합니다. 이렇게 하면 기존 클래스를 재 사용할수 있습니다.

클래스 상속 형식

open class Super{ // 상속할 수 있게 open 키워드 이용
}
class Sub: Super(){ // Super를 상속받아 Sub 클래스 선언

상속 관계에서 상속 대상이 되는 클래스를 상위 클래스라고 하고, 상속받는 클래스를 하위 클래스라고 합니다. 코틀린의 클래스는 기본적으로 다른 클래스가 상속할 수 없습니다. 다시 말해 class Super{} 처럼 클래스를 선언하면 다른 클래스에서 Super 클래스를 상속할 수 없습니다. 만약 다른 클래스에서 상속할 수 있게 선언하려면 open 키워드를 사용해야합니다. 즉 open class Super {} 라고 선언하면 Super 클래스의 상속을 허용합니다.
하위 클래스를 선언할 때는 이름 뒤에 콜론(:)을 입력하고 상속받을 상위 클래스 이름을 입력합니다. class Sub: Super() {} 코드는 Super 클래스를 상속받아 Sub 클래스를 선언한 구문입니다.
상위 클래스를 상속받은 하위 클래스의 생성자에서는 상위 클래스의 생성자를 호출해야 합니다. class Sub: Super() {}코드에서 Super()는 Super 클래스를 상속받으면서 이 클래스의 매개변수가 없는 생성자를 호출합니다. 만약 매개변수가 있는 상위 클래스의 생성자를 호출할 때는 다음처럼 매개변수 구성에 맞게 인자를 전달해야 합니다.

매개변수가 있는 상위 클래스의 생성자 호출

open class Super(name: String){
}
class Sub(name: String): Super(name){
}

상위 클래스의 생성자 호출문을 꼭 클래스 선언부에 작성할 필요는 없습니다. 만약 하위 클래스에 보조 생성자만 있다면 상위 클래스의 생성자를 다음처럼 호출할 수 있습니다.

하위 클래스에 보조 생성자만 있는 경우 상위 클래스의 생성자 호출

open class Super(name: String){
}
class Sub: Super{
   constructor(name: String): super(name){
   }
}

Kotlin 안드로이드 앱 프로그래밍 (9)

주 생성자
코틀린에서의 클래스는 생성자를 주 생성자와 보조 생성자로 구분합니다. 한 클래스 안에 주 생성자만 선언할 수도 있고 보조 생성자만 선언할 수도 있습니다. 물론 둘 다 선언 가능합니다.
주 생성자는 constructor 키워드로 클래스 선언부에 선언합니다. 주 생성자 선언은 필수는 아니며 한 클래스에 하나만 가능합니다.

주 생성자 선언

class User constructor() {
}

주 생성자를 선언할 때 constructor 키워드는 생략할 수 있습니다.

constructor 키워드 생략 예

class User() {
}

만약 개발자가 클래스의 주 생성자를 선언하지 않으면 컴파일러가 매개변수가 없는 주 생성자를 자동으로 추가합니다.

매개변수가 없는 주 생성자 자동 선언

class User() {
}

주 생성자의 매개변수
주 생성자를 선언할 때 필요에 따라 매개변수를 선언할 수도 있습니다.

주 생성자의 매개변수

class User(name: String, count: Int){
}

앞의 코드는 User 클래스를 선언하면서 주 생성자에 매개변수를 2개 선언하였습니다. 그러면 객체를 생성할 때 매개변수의 타입과 개수에 맞는 인자를 전달해야 합니다.

매개변수가 있는 생성자 호출

val user = User("kkang", 10)

주 생성자의 본문 – init 영역
주 생성자를 이용해 객체를 생성할 때 특정한 로직을 수행할 수 있습니다. 그런데 주 생성자의 실행 영역인 본문을 다음처럼 추가하면 오류가 발생합니다.

주 생성자 중괄호 때문에 오류 발생

class User(name: String, count: Int){
     // 주 생성자 본문
}{   // 오류
     // 클래스 본문
}

보통 클래스나 함수의 본문(실행 영역)은 중괄호 {}로 감싸지만 주 생성자에는 {}를 추가할 수 없습니다. 왜냐하면 주 생성자는 클래스 선언부에 있기 때문입니다. 이럴 때 init 키워드를 이용해 주 생성자의 본문을 구현할 수 있습니다.
코틀린의 클래스 안에서 init 키워드로 지정한 영역은 객체를 생성할 때 자동으로 실행됩니다. 클래스에서 init 영역은 꼭 선언할 필요는 없으므로 주 생성자의 본문을 구현하고 싶을 때 사용합니다.
init 영역은 주 생성자뿐만 아니라 잠시 후에 다루는 보조 생성자로 객체를 생성할 때도 실행됩니다. 하지만 보조 생성자는 클래스 안에 선언하므로 {}를 이용해 본문을 지정할 수 있습니다. 따라서 init 영역은 일반적으로 주 생성자의 본문을 구현하는 용도로 사용합니다.

init 키워드로 주 생성자의 본문 지정

class User(name: String, count: Int){
   init {
      println("i am init...")
   }
}
fun main() {
   val user = User("kkang", 10)
}

>> 실행결과
i am init...

User라는 클래스에 주 생성자를 선언하고 클래스 본문에 init 영역을 두어 주 생성자의 본문을 작성하였습니다. 이렇게 하면 main() 함수에서 User 클래스의 객체를 생성할 때 init 영역에 작성한 코드가 자동으로 실행됩니다.

생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법
생성자의 매개변수는 기본적으로 생성자에서만 사용할 수 있는 지역 변수입니다.

생성자의 매개변수를 init 영역에서 사용하는 예

class User(name: String, count: Int){
   init{
      println("name : $name, count : $count") // 성공
   }
   fun someFun(){
      println("name : $name, count : $count") // 오류
   }
}

위 코드에서는 주 생성자에 name, count 매개변수를 선언하였습니다. 그리고 이 변수를 init 영역과 someFun() 이라는 함수에서 사용하려고 합니다. 생성자를 호출할 때 init 영역이 실행되므로 이곳에서 생성자의 매개변수에 접근할 수 있습니다.
하지만 생성자의 매개변수는 지역 변수이므로 다른 함수에서는 사용할 수 없습니다. 만약 생성자의 매개변수를 클래스의 멤버 변수처럼 다른 함수에서 사용해야 한다면 다음처럼 작성해야 합니다.

생성자의 매개변수를 다른 함수에서 사용하는 예

class User(name: String, count: Int){
   // 클래스 멤버 변수 선언
   var name: String
   var count: Int
   init{
      // 클래스 멤버 변수에 생성자 매개변숫값 대입
      this.name = name
      this.count = count
}
   fun someFun(){
      println("name : $name, count : $count") // 성공
   }
}
fun main(){
   val user = User("kkang", 10)
   user.someFun()
}

>> 실행결과
name : kkang, count : 10

클래스의 멤버 함수 someFun()에서 생성자의 매개변수를 이용하고자 클래스의 멤버 변수를 선언하고 주 생성자의 본문인 init 영역에서 매개변숫값을 클래스의 멤버 변수에 대입하였습니다.
그런데 이 방법 말고도 생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법이 있습니다. 이렇게 하면 코드를 조금 더 간단하게 작성할 수 있습니다. 주 생성자의 매개변수는 생성자 안에서만 사용할 수 있는 지역 변수지만 매개변수를 var나 val 키워드로 선언하면 클래스의 멤버 변수가 됩니다.

생성자의 매개변수를 클래스의 멤버 변수로 선언하는 방법

class User(val name: String, val count: Int){
   fun someFun(){
      println("name : $name, count : $count") // 성공
   }
}
fun main(){
   val user = User("kkang", 10)
   user.someFun()
}

>> 실행결과
name : kkang, count : 10

원래 함수는 매개변수를 선언할 때 var나 val 키워드를 추가할 수 없습니다. 그런데 주 생성자에서만 유일하게 var나 val 키워드로 매개변수를 선언할 수 있으며 이렇게 하면 클래스의 멤버 변수가 됩니다. 따라서 위 코드에서 생성자의 매개변수 name과 count를 someFun()이라는 멤버 함수에서 사용할 수 있습니다.

(미완성) DU Attend 서비스에서 식별 정보 관리

서론

DU Attend 는 QR코드를 미리 휴대폰에 저장해두고 앉은자리에서 편리하게 출석체크 해 주는 서비스입니다.

시대의 흐름에 따라 개인정보의 중요성이 커져가면서 본인의 데이터가 안전한지 궁금해하는 사용자들이 많습니다. 서비스를 만드는 입장에서는 귀찮아할 일이 아니라 데이터 관리를 어떻게 하는지 적극적으로 알려드려야 할 부분 같습니다. 이러한 문화가 많이 퍼졌으면 좋겠습니다.

수집하는 식별정보

DU Attend 서비스에서는 사용자의 정보를 최소한으로 수집합니다. 순수 무료 목적으로 만들어졌기 때문에 다음과 같은 정보만 수집합니다.

  1. 아이디 -> 학번으로 유도
  2. 암호

DU Attend 서비스에서 다른 서비스들과 다른 점은 아이디를 학번이랑 동일하게 해야 합니다.
사실 학번을 사용하지 않고 무작위 8자리 숫자를 사용해도 됩니다. 학번을 사용하는 이유는 출석 시스템에서 학번을 사용하기 때문입니다. 무작위 8자리로 가입하거나 다른 사람의 학번으로 가입하게 되면 출석 기능이 제대로 동작하지 않을 것 입니다.

출석 QR과 관련된 정보는 나중에 다른 글로 정리하겠습니다.

암호는 tigers(종합정보시스템) 암호가 아닙니다. DU Attend 에서 개별적으로 사용하는 암호입니다. 현재 암호 정책은 최소 4자리 최대 36자리 로 제한하고 있습니다. 숫자 4자리로 해도 되지만 좀 더 강력한 암호를 사용하시면 좋겠습니다. 암호의 중요성은 잘 아실 거라 생각합니다.

암호 저장방법

암호 정보는 해시 함수를 사용하여 저장합니다. 해시 함수란 위키백과에 잘 정리되어 있습니다.

해시함수(hash函數)는 하나의 주어진 출력에 대하여 이 출력으로 사상시키는 하나의 입력을 찾는 것이 계산적으로 불가능하고, 하나의 주어진 입력에 대하여 같은 출력으로 사상시키는 또 다른 입력을 찾는 것이 계산적으로 불가능하다는 두 가지 성질을 만족하면서 임의의 비트열을 고정된 길이의 비트열로 사상시키는 함수이다.

한마디로 입력값을 해시 함수에 통과시키면 무작위 문자로 보이는 결과물이 출력됩니다. 해시 함수는 출력값으로 입력값을 유추할 수 없어야 합니다.

그리고 해시 함수의 알고리즘은 널리 알려져 있습니다. 알고리즘이 공개되어 있어도 많은 곳에서 사용한다는 것은 안전하다는 뜻이기도 합니다.

DU Attend 에서 사용하는 해시 함수는 bcrypt 입니다. bcrypt 의 장점은 많은 언어에서 구현되어 있어 사용하기 편리하고(개발 속도가 빠르고) 아직 알려진 문제가 없다는 점 입니다. 그리고 해시를 생성할 때 마다 다른 출력값이 나온다는 점 입니다.

https://bcrypt-generator.com/ 에서 쉽게 해볼 수 있습니다. 아무 값이나 입력하고 Encrypt 버튼을 여러번 눌러 보세요. 생성할 때 마다 다른 값이 나옵니다. 자주 쓰는 패스워드를 해커가 알고 있어도 같은 결과값을 찾기는 불가능에 가깝습니다. 이것을 salt 라고 하는데요… 여기에서 보시죠.
https://ko.wikipedia.org/wiki/%EC%86%94%ED%8A%B8_(%EC%95%94%ED%98%B8%ED%95%99)

이런 방법으로 암호를 저장하면 안전합니다. 하지만 아직 많이 남았습니다.

암호 정보의 흐름

로그인 페이지에 암호를 입력하고 로그인에 성공하고 메인페이지를 보기까지 서버에서는 많은 일이 일어납니다.

출장세차 부를려다 현다이에서 내계정 날려버린 썰푼다.

일단 굉장히 빡친 상태라 글이 매끄럽지 않은거 이해부탁한다.

그저께 마이 현다이 어플로 출장 세차를 예약했다. 예약하고 확정 지을려고 하니 계속 아래 그림처럼 시스템 오류가 뜨는거다. 그래서 조금 있다가 하면 되겠지했는데 안되는거다.

마이 현다이 어플 시스템 오류

계속 오류나서 고객센터에 문의할려고 하니 이시키들 고객센터 연락처도 어플에 안넣어 놓았다. 현다이에 문의하니 위탁 업체라고 따로 문자로 전화번호 얻었다.

시8럼들아 전화받기 싫으면 1:1 문의사항이라도 만들어줘

현대자동차 고객센터로 위탁업체 전화번호 받음

근데 여기다 전화해보니 위탁 진행이라고 담당 연락처 다른거라고 따로 연락 준다고 한다. 그게 아래 전화번호다.

위착업체랑 주고받은 전화

어제(10일) 18시쯤(퇴근 직전) 전화와서 문의사항 확인했다고 빨리 해결해준다고 했다. 근데 오늘(11일) 아침에 전화와서 내계정이 DB에 검색이 안된다는 거다.

그리고 계정 메일 주소를 알려주고 오후에 전화와서 메일 주소도 없다고 나온다는거다. 그래서 캡쳐본을 문자로 보내줬다. 로그인 된 화면을!!

마 현다이에 로그인 되어있는거 안보이냐!!!

그러고 시간이 지나서 전화가 왔다. 지네들도 처음 뜨는오류고 이런 케이스가 처음이라고 한다. (DB에 승우기계정 날림)

마지막 오늘 퇴근 직전에 전화와서 미안하다고 본사에 문의하고 연락 주겠다고 계정 비밀번호도 달라고 하는거다. 그래서 줬다.

승우기 계정 마이현대 만든놈한테 털림

내일(12일) 오전중으로 처리해서 알려준다고 하니 일단 기다려본다. 다음주에 뒷이야기 알려주겠다.

Spring 입문 (JPA에서의 동적 쿼리)

현재 상품 주문 시스템에서 검색 기능을 활성화 하기 위해서는 동적 쿼리를 해결해야한다.

방법 1. JPQL로 처리

public List<Order> findAllByString(OrderSearch orderSearch) {
        //language=JPAQL
        String jpql = "select o From Order o join o.member m";
        boolean isFirstCondition = true;

        //주문 상태 검색
        if (orderSearch.getOrderStatus() != null) {
            if (isFirstCondition) {
                jpql += " where";
                isFirstCondition = false;
            } else {
                jpql += " and";
            }
            jpql += " o.status = :status";
        }

        //회원 이름 검색
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            if (isFirstCondition) {
                jpql += " where";
                isFirstCondition = false;
            } else {
                jpql += " and";
            }
            jpql += " m.name like :name";
        }
        TypedQuery<Order> query = em.createQuery(jpql, Order.class)
                .setMaxResults(1000); //최대 1000건
        if (orderSearch.getOrderStatus() != null) {
            query = query.setParameter("status",
                      orderSearch.getOrderStatus());
        }
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            query = query.setParameter("name", 
                      orderSearch.getMemberName());
        }
        return query.getResultList();
    }

JPQL 쿼리를 문자로 생성하기는 번거로움
실수로 인한 버그가 충분히 발생할 수 있음

방법 2. JPA Criteria로 처리

public List<Order> findAllByCriteria(OrderSearch orderSearch) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Order> cq = cb.createQuery(Order.class);
        Root<Order> o = cq.from(Order.class);
        Join<Order, Member> m = o.join("member", JoinType.INNER);
        List<Predicate> criteria = new ArrayList<>();

        //주문 상태 검색
        if (orderSearch.getOrderStatus() != null) {
            Predicate status = cb.equal(o.get("status"),
                    orderSearch.getOrderStatus());
            criteria.add(status);
        }

        //회원 이름 검색
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            Predicate name =
                    cb.like(m.<String>get("name"), "%" +
                            orderSearch.getMemberName() + "%");
            criteria.add(name);
        }
        cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
        TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000);
        return query.getResultList();
    }

JPA Criteria는 JPA 표준 스펙이지만 실무에서 사용하기에 너무 복잡
결국 다른 대안이 필요
해결책 ==> Querydsl 제시

방법 3. Querydsl로 처리

public List<Order> findAll(OrderSearch orderSearch){
        QOrder order = QOrder.order;
        QMember member = QMember.member;

        return query
                .select(order)
                .from(order)
                .join(order.member, member)
                .where(statusEq(orderSearch.getOrderStatus())),
                        nameLink(orderSearch.getMemberName()))
                .limit(1000)
                .fetch();
    }

    private BooleanExpression statusEq(OrderStatus statusCond){
        if(statusCond == null){
            return null;
        }
        return  order.status.eq(statusCond);
    }

    private BooleanExpression nameLink(String nameCond){
        if (!StringUtils.hasText(nameCond)){
            return null;
        }
        return member.name.link(nameCond);
    }

훨씬 간결한 방법으로 동적 쿼리 문제를 해결
dsl에 대한 학습이 필수적으로 필요해 보임

Kotlin 안드로이드 앱 프로그래밍 (8)

For과 While
코틀린에서도 반복문 for과 while을 지원합니다. for 문은 제어 변숫값을 증감하면서 특정 조건이 참일 때까지 구문을 반복해서 실행합니다. 이때 for 문의 조건에는 주로 범위 연산자인 in을 사용합니다.

for-in 반복문

fun main(){
   var sum: Int = 0
   for (i in 1..10){
      sum += i
   }
   println(sum)
}
>> 실행결과
55 (1부터 10까지 순서대로 합함)

위 소스는 for 문을 이용해 1부터 10까지 더하기를 수행합니다. for 문의 소괄호 안에 있는 i in 1..10 코드는 1부터 10까지 1씩 증가하면서 for 문의 실행 영역을 반복하라는 의미입니다. 따라서 총 10번 반복해서 실행합니다.
for 문의 조건은 이와 같이 단순히 1씩 증가 또는 감소하게 만들 수도 있고 2씩 증가하게 만드는 등 다양하게 작성할 수 있습니다.

for (i in 1..10) {…} -> 1부터 10까지 1씩 증가
for (i in 1 until 10) {…} -> 1부터 9까지 1씩 증가(10은 미포함)
for (i in 2..10 step 2) {…} -> 2부터 10까지 2씩 증가
for (i in 10 downTo 1) {…} -> 10부터 1까지 1씩 감소

증감 조건을 숫자로 명시하지 않고 컬렉션 타입의 데이터 개수만큼 반복하게 할 수도 있습니다.

반복 조건에 컬렉션 타입 활용

fun main(){
   var data = arrayOf<Int>(10, 20, 30)
   for (i in data.indices){
      print(data[i])
      if(i !== data.size - 1) print(",")
   }
}
>> 실행결과
10,20,30

배열의 크기만큼 for 문을 반복하게 작성한 소스입니다. indices는 컬렉션 타입의 인덱스값을 의미하므로 for 문을 반복하면서 0,1,2 값을 i에 대입합니다. 만약 for 문을 반복하면서 인덱스와 실제 데이터를 함께 가져오려면 withIndex() 함수를 이용합니다.

인덱스와 데이터를 가져오는 withIndex() 함수

fun main(){
   var data = arrayOf<Int>(10,20,30)
   for ((index, value) in data.withIndex()){
      print(value)
      if (index !== data.size-1) print(",")
   }
}
>> 실행결과
10,20,30

for 문 외에 while 문을 이용해 반복문을 작성할 수도 있습니다. while 문은 조건이 참이면 중괄호 {}로 지정한 영역을 반복해서 실행합니다.

while 반복문

fun main(args: Array<String>){
   var x = 0
   var sum1 = 0
   while (x < 10) {
      sum += ++x
   }
   println(sum1)
}
>> 실행결과
55

클래스와 생성자
클래스 선언
코틀린에서 클래스는 class 키워드로 선언합니다. 다음 코드에서 class User 부분이 클래스의 선언부이며 중괄호 {} 영역이 본문입니다. 만약 클래스의 본문에 입력하는 내용이 없다면 {}를 생략할 수 있습니다.

[ 클래스 선언 – class User { } ]

클래스의 멤버는 생성자, 변수, 함수, 클래스로 구성됩니다. 이 중에서 코틀린의 생성자는 constructor 라는 키워드로 선언하는 함수입니다. 그리고 클래스 안에 다른 클래스를 선언할수도 있습니다.

클래스의 멤버

class User {
   var name = "kkang"
   constructor(name: String){
      this.name = name
   }
   fun someFun(){
      println("name : $name")
   }
   class SomeClass {}
}

클래스는 객체를 생성해 사용하며 객체로 클래스의 멤버에 접근합니다. 그런데 코틀린에서는 객체를 생성할 때 new 키워드를 사용하지 않습니다.

객체 생성과 멤버 접근
val user = User("kim")
user.someFun()

User(“kim”)이 객체를 생성하는 구문이며 클래스 이름과 같은 함수로 객체를 생성합니다.
객체를 생성할 때 생성자가 자동으로 호출되므로 소괄호 안에 전달한 인자는 클래스에 선언된 생성자의 매개변수와 들어맞아야 합니다. 앞에서 작성한 User 클래스의 생성자는 constructor(name: String)이므로 문자열 데이터를 전달받는 매개변수가 있습니다. 따라서 객체를 전달할 때 User(“kim”)처럼 문자열 데이터를 전달해 주어야 합니다.

정보처리기사 자격증 문제풀이 (1)

정보처리기사 자격증을 위해서 2020년 개정 이후의 기출 문제를 주마다 풀어보겠습니다. 이번 주는 2020.06.06의 기출 문제 중 1과목(20문제)을 풀어봤습니다.

1과목 : 소프트웨어 설계 (20문제)

1. 요구 사항 검토 방법

  • 동료 검토 : 작성자가 명세서 내용 설명 동료들이 결함 발견하는 형태
  • 워크 스루 : 검토 회의 전 명세서를 미리 배포하여 사전 검토 후 짧은 검토 회의를 통해 오류 조기 검출
  • 인스펙션  : 명세서 작성자를 제외한 다른 검토 전문가들이 확인하면서 결함을 발견하는 형태

2. 일련번호를 부여하는 방식

  • 연상 코드: 항목의 명칭이나 약호와 관계 있는 숫자, 문자, 기호를 이용하여 코드를 부여하는 방법
  • 블록 코드: 대상 항목에서 공통적인 것을 블록으로 구분하고 블록 내에 일련 번호를 부여하는 방법
  • 순차 코드: 일정 기준에 따라 최초의 자료부터 일련번호를 부여하는 방법
  • 표의 숫자 코드: 길이 넓이 부피 등 항목의 성질의 물리적인 수치를 그대로 코드에 적용시키는 방법

3. 객체지향 프로그램에서 데이터를 추상화하는 단위

= 클래스

4. 데이터 흐름도(DFD)의 구성요소

  • 프로세스(Process), 자료 흐름(Flow), 자료 저장소(Data Store), 단말(Terminal)

5. 성능특성 분석에 사용되는 측정 항목

  • 경과시간(Turnaround TIme)
  • 사용률(Utilization)
  • 응답시간(Response TIme)
  • 가용성(Availability)

6. UML 확장 모델에서 스테레오 타입 객체를 표현하는 기호

= << >>

7. GoF(Gang of Four)의 디자인 패턴에서 행위 패턴에 속하는 것

  •  Builder : 작게 분리된 인스턴스를 건축 하듯이 조합하여 객체를 생성한다
  • Visitor : 각 클래스들의 데이터 구조에서 처리 기능을 분리하여 별도의 클래스로 구성한다
  • Prototype : 원본 객체를 복제하는 방법으로 객체를 생성한다.
  • Bridge : 구현부에서 추상층을 분리하여, 서로가 독립적으로 확장할 수 있도록 구성한다.
생성패턴 : 객체의 생성과 관련된 패턴
구조패턴 : 클래스나 객체들을 조합하여 더 큰 구조로 만들 수 있게 해주는 패턴
행위패턴 : 클래스나 객체들이 상호작용하는 방법이나 책임 분배 방법을 정의하는 패턴

Builder , Prototype - 생성패턴
, Bridge - 구조패턴

8. 자료 사전에서의 기호

정의 =
구성,연결 +
반복 { }
주석 **
선택 [ㅣ]
생략 ( )

9. 데이터를 감시하고 제어하는 미들웨어

  • TP monitor: 트랜잭션 처리를 감시/제어하는 미들웨어
  • ORB: object request broker, 객체 간 메시지 전달을 지원하는 미들웨어
  • RPC: remote procedure call 원격 절차 호출 또는 원격 프로시저 호출

10. UI 설계 원칙

  • 직관성 : 누구나 쉽게 이용하고 쉽게 사용할 수 있어야 함
  • 유효성 : 정확하고 완벽하게 사용자의 목표가 달성될 수 있도록 제작
  • 학습성 : 초보와 숙련자 모두가 쉽게 배우고 사용할 수 있게 제작
  • 유연성 : 사용자의 인터랙션을 최대한 포용하고, 실수를 방지할 수 있도록 제작

11. XP(eXtreme Programming)의 5가지 가치

  • 용기(Courage) : 고객의 요구사항 변화에 능동적인 대처
  • 단순성(Simplicity) : 부가적 기능, 사용되지 않는 구조와 알고리즘 배제
  • 커뮤니케이션(Communication) : 개발자, 관리자, 고객 간의 원활한 의사소통
  • 피드백(Feedback) : 지속적인 테스트와 반복적 결함 수정, 빠른 피드백
  • 존중(Respect) : 모든 프로젝트 관리자는 팀원의 기여를 존중

12. UML 모델에서 사용하는 구조 다이어그램(Structural Diagram)

  • 구조 다이어그램은 클래스(Class), 객체(object), 복합체 구조(Composite Structure), 배치(Deployment), 컴포넌트(Component), 패키지(Package) 다이어그램이 포함

13. 소프트웨어 개발 방법 중 요구사항 분석

  • 비용과 일정에 대한 제약설정
  • 타당성 조사
  • 요구사항 정의 문서화

14. 럼바우(Rumbaugh)의 객체지향 분석 절차

  • 객체 모델링 ➔ 동적 모델링 ➔ 기능 모델링

15. 공통 모듈에 대한 명세 기법

- 정확성 : 해당 기능이 실제 시스템 구현시 필요 유무를 알 수 있도록 정확하게 작성
- 명확성 : 해당 기능에 대해 일관되게 이해하고 한가지로 해석될 수 있도록 작성
- 완전성 : 시스템이 구현될 때 필요하고 요구되는 모든 것을 기술
- 일관성 : 공통 기능 간에 상호 충돌이 없도록 작성
- 추적성 : 공통 기능에 대한 요구사항 출처와 관련 시스템 등의 유기적 관계에 대한 식별이 가능하도록 작성

16. 객체지향 기법의 관계 용어

  • 집단화 (is part of): 클래스 간의 구조적인 집약 관계 “클래스 A는 클래스 B와 클래스 C로 구성된다”
  • 일반화 (is a) :클래스들 간의 개념적인 포함 관계  “자식 클래스 A는 부모 클래스 B의 일종이다.”
  • 캡슐화 : 속성 (데이터)과 메소드(연산) 을 하나로 묶어서 객체로 구성된다.
  • 추상화 : 공통 성질을 추출하여 수퍼클래스로 구성한다. 또한 객체 중심의 안정된 모델을 구축 가능 하며 현실 세계를 자연스럽게 표현한다. 장점으로 분석의 초점이 명확해진다.

17. CASE가 갖고 있는 주요 기능

1. S/W의 생명주기 전(모든)단계의 연결
2. 모델들 사이의 모순검사
3. 오류검증
4. 자료흐름도 등 다이어그램 작성
5. 다양한 소프트웨어 개발 모형지원
6. 시스템 문서화 및 명세화를 위한 그래픽 지원

18. DBMS 분석시 고려사항

1. 무결성(가용성)
2. 일관성(상호호환성)
3. 회복
4. 보안
5. 효율성(성능)
6. 데이터베이스 확장

19. HIPO(Hierarchy Input Process Output)에 대한 설명

  • 하향식 소프트웨어 개발을 위한 문서화 도구이다.
  • HIPO 차트 종류에는 가시적 도표, 총체적 도표, 세부적 도표가 있다.
  • 기능과 자료의 의존 관계를 동시에 표현할 수 있다.
  • 보기 쉽고 이해하기 쉽다.

20. 객체지향 분석 방법론 중 E-R 다이어그램을 사용하는 방법

  • Coad 와 Yourdon 방법: E-R다이어그램 사용 객체 행위 모델링 및 객체 구조 식별 및 주체 속성 및 관계 서비스 정의
  • Booch 방법: 클래스와 객체 식별 및 의미 관계 식별
  • 럼바우(Rumbaugh) 기법: 소프트웨어 구성요소를 그래픽 표기법을 이용하여 모델링 / 객체모델링 동적 모델링 기능 모델링