반응형

# 액티비티 Activity

안드로이드 구성요소 중 화면을 담당하는 파트

여러 액티비티들을 만들어 두고 목적에 따라 

유기적으로 작동하도록 만들려면 인텐트가 절대적으로 필요

즉, 한 액티비티에서 다른 액티비티를 실행하거나 실행한

액티비티에 데이터를 전달하는 등의 작업을 하려면 

인탠트를 사용해야 함

 

# 인탠트 intent

다른 액티비티를 실행하거나 데이터를 전달할 수 있는

안드로이드 구성요소 중 하나

 

# 리스트뷰 

사용자가 정의한 데이터 목록을 항목단위로 구성하여 화면에 출력하는 구성요소

목록의 항목들은 세로방향으로 나열되며 항목 수가 많으면 

스크롤 기능을 사용해서 나머지 항목들은 숨겨둘 수 있음

안드로이드 구성요소중 사용빈도가 가장 많은 위젯

 

한편, 리스트뷰에 데이터를 추가해서 화면에 표시하려면 반드시 어댑터를 사용해야만 함

즉, 이것을 통해 model과 view를 연결해서 사용자가 원하는 UI형태로 데이터를 표현할 수 있기 때문

 

하지만, 스크롤 할때 마다 화면에 보여줄 항목을 다시 그리기 때문에

성능상의 오버헤드가 발생 - 현재는 RecyclerView를 사용할 것을 추천

 

# 직렬화와 역질렬화

serialization, marshaling

생성된 데이터 객체유형을 그대로 유지하면서 파일에 저장하는 기법

즉, 데이터를 텍스트 형태로 풀어서 파일에 저장하는 것이 아니고 

메모리상에 만들어진 형태 그대로 파일에 저장하는 것을 의미 

 

반면, 파일에 저장된 객체형태의 데이터를 메모리에 그대로 옮기는 과정을 역직렬화라고 함

직렬화 / 역질렬화를 하려면 클래스 정의시 Serializable 이라는 인터페이스를 구현해야 함

 

# 안드로이드에서의 직렬화 / 역질렬화 

자잡에서는 Serializable을 구현해서 만든 클래스로 직렬화를 할 수 있음

단, 안드로이드에서는 자바의 Serializable보다 복잡하지만

Paracelable 을 구현해서 만든 클래스로 직렬화하는 것을 추천함 

 

#Paracelable 클래스 구현방법

1. Paracelable 인터페이스를 구현

2. writeToParcel/describeContents메서드 , CREATOR 싱글톤 클래스등을 재정의 해줌

3. 안드로이드 스튜디오에서는 자동으로 생성해 줌 : Add Parcelable Implementation 메뉴 사용

 

# Parcelable 객체 보내고 받기

하나의 액티비티에서 다른 액티비티로 데이터를 전달해야 하는 경우 

보내는 측에서는 putExtra메서드를 이용해서 보낼 데이터를 셋팅하고

받는 측에서는 getExtra메서드를 이용해서 받은 데이터를 가져올 수 있음 

 

한편, 객체형 데이터를 보내고 받아야 하는 경우

보내는 측에서는 putExtra메서드를 이용해서 보낼데이터를 셋팅하고 

받는 측에서는 getParacelableExtra메서드를 이용해서 받은 데이터를 가져올 수 있음

 

# RecyclerView 위젯

안드로이드 5.0(롤리팝)에서 처음 소개 되었음 

ListView 위젯보다 유연하고 성능이 향상된 위젯으로 구글에서 추천 위젯

즉, 화면의 뷰가 바뀌면 전체 데이터를 다시 불러와서 화면에 출력해야 하는 비효율 적인 면이 있었음

 

ViewHolder 패턴을 이용해서 어댑터를 정의할 것이 강제(리스트뷰에서는 선택사항)

LayoutManager를 이용해서 다양한 모양의 뷰를 커스텀화 할 수 있음

ItemDecoration 클래스를 이용해서 item 간의 간격을 조절할 수 있음

ItemAnimator 클래스를 이용해서 item 삽입, 제거 , 이동시 애니메이션 효과 적용 가능

리스트뷰에 비해 이벤트 처리 강화

 

#RecyclerView의 레이아웃 매니져

RecyclerView 위젯에 표시된 항목을 어떤 형태로 배치할것인지 결정하는 데 사용하는 객체

LinearLayoutManager : 수평/수직 형태로 항목을 배치

GridLayoutManager : 격자형으로 항목을 배치

StaggeredGridLayoutManager : itemview마다 크기가 다른 목록으로 항목 배치

 

#RecyclerView에 클릭 애니메이션 효과 추가하기

sj_listview.xml 의 최상위 root 요소에 다음 내용 추가 

 

android:background="?attr/selectableitemBackground"

 

 

 

실습) 명화 투표 - 투표결과 

1번액티비티에서 9가지 그림을 띄워놓는 액티비티를 만들고 각 그림 클릭시 투표수가 증가하여 결과보기 버튼을 누르면

2번 액티비티에서 각그림에 대한 평점과 투표수에 맞는 별점을 레이팅하는 것 .

 

 

위의 기능을 구현하기 위해 뇌피셜로 순서를 그려보았을때 

 

일단 layout 상에서는 그림을 보여줄 imageView가 9개가 필요하고 

클래스파일에서 이미지의 이름 9개를 배열로 받아줄 변수 하나 

마찬가지로 각 이미지의 투표수를 담아줄 투표수 배열 변수 하나

마지막으로 각 이미지의 클릭이벤트를 걸기위해 각 이미지 고유 아이디를 가져와서 이벤트를

걸어줘야하기 때문에 받아줄 변수가 하나더 필요하다.

 

MPActivity

package imlsw96.helloactivity

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.Toast




class MPActivity : AppCompatActivity() {
    var vote = IntArray(9) // 선호도 배열

    var imgName = arrayOf("독서하는 소녀", "꽃장식 모자 소녀", "부채를 든 소녀",
        "이레느깡 단 베르양", "잠자는 소녀", "테라스의 두 자매", "피아노 레슨", "피아노 앞의 소녀들",
        "해변에서") // 이미지 이름 배열

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_m_p)
        title = "명화 선호도 조사"
        // 이미지 클릭시 선호도 정보를 저장할 배열 선언
        var iv = arrayOfNulls<ImageView>(9) // 이미지버튼 객체 배열
        var ivid = arrayOf(R.id.iv1, R.id.iv2, R.id.iv3, R.id.iv4, R.id.iv5, R.id.iv6, R.id.iv7, R.id.iv8, R.id.iv9) // 이미지버튼 ID 배열

        // 이미지 클릭시 각 이미지의 투표수를 증가시킴
        // 또한, 해당 이미지의 이름과 누적 투표수를 Toast로 출력함함
        // 따라서, 각 이미지 버튼에 클릭이벤트를 추가해 둠
        for (i in ivid.indices) {
            iv[i]=findViewById<ImageView>(ivid[i])
            iv[i]!!.setOnClickListener{
                vote[i]++
                Toast.makeText(applicationContext,"${imgName[i]} 투표수 : ${vote[i]}",Toast.LENGTH_SHORT).show()
            }
        }
    }
    fun goResult(v: View) {
        var intent = Intent(applicationContext, MP2Activity::class.java)
        intent.putExtra("vote",vote)
        intent.putExtra("imgName",imgName)
        // 다른 액티비티를 실행하기 위해 인텐트 객체 생성
        startActivity(intent)
        // 생성한 인텐트를 실행함
    }

}

 

MP2Activity

package imlsw96.helloactivity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.RatingBar
import android.widget.TextView

class MP2Activity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_m_p2)
        title="선호도 조사 결과"
        // MPActivity에서 넘겨준 데이터를 담아둘 배열 변수 선언
        var rbid = arrayOf(R.id.rb1, R.id.rb2, R.id.rb3, R.id.rb4,
            R.id.rb5, R.id.rb6, R.id.rb7, R.id.rb8, R.id.rb9)

        // MPActivity에서 넘겨준 투표수를 반영할 레이팅바 객체를 저장할 배열 변수 선언
        var rbar = arrayOfNulls<RatingBar>(9)

        // 배열 변수에 실제 레이팅바 객체를 초기화함
        for(i in rbid.indices){
            rbar[i] = findViewById(rbid[i])
            rbar[i]!!.setIsIndicator(false)
            rbar[i]!!.isClickable = false
            rbar[i]!!.isClickable = false
        }



        // MPActivity에서 넘겨준 객체를 받아서 적절한 변수에 대입
        var intent = intent
        var vote = intent.getIntArrayExtra("vote")
        var imgName = intent.getStringArrayExtra("imgName")

        var tv = arrayOf(R.id.tv1,R.id.tv2,R.id.tv3,R.id.tv4,R.id.tv5,R.id.tv6,R.id.tv7,R.id.tv8,R.id.tv9)
        var realtv = arrayOfNulls<TextView>(9)
        // 초기화하 레이팅바 객체에 투표수를 반영함
        for (i in rbid.indices) {
            realtv[i] = findViewById(tv[i])
            realtv[i]!!.setText(imgName!![i])
            rbar[i]!!.setRating(vote!![i].toFloat())

        }

    }
    fun goMain(v: View) {
        finish()
    }
}

 

 

 

 
반응형
반응형

# 안드로이드 저장공간 

view - tool window - device file explorer

 

내부 저장공간

앱을 설치할 때 앱만을 위한 전용공간이 할당됨

해당 앱에서만 접근 가능

앱을 삭제하면 해당 공간도 삭제됨 

/data/data/패키지 이름

 

# 내부 저장공간 데이터 쓰기/읽기

FileInputStream, FileOutputStream 클래스를 이용해서

내부 저장소에 데이터를 저장하거나 읽어올 수 있음

 

1. MainPractice생성 

2. layout 버튼 내부 저장공간 데이터 쓰기/읽기 2개 생성

3. MainPractice내부에 FileInputStream / FileOutputStream을 이용해 파일 생성 /읽기

 

 

 

package imlsw96.hellofiles

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.view.View
import android.widget.Toast
import java.io.FileInputStream
import java.io.FileOutputStream

class MainPractice : AppCompatActivity() {
    var fname = "Hello.txt"     // 생성할 파일 이름을 담을 변수
    var TAG = "MainPractice" // 로그확인을 위해 현재 액티비티 이름을 담는 변수 생성
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_practice)
    }
    // 내부 저장소 파일 쓰기 메서드
    fun internalFileWrite2 (v: View) {
        // 앱전용 쓰기 경로에 쓰기모드를 연다.
        var ofs : FileOutputStream = openFileOutput(fname, Context.MODE_PRIVATE)

        var contents = "파일에 쓸 내용을 여기다 적는다. 가나다라마바사아자차카타파하"

        ofs.write(contents.toByteArray())
        ofs.close()

        Toast.makeText(applicationContext,"파일 쓰기 완료 ! ",Toast.LENGTH_SHORT).show()

        Log.d(TAG,"파일저장 완료!")
    }

    // 내부 저장소 파일 불러오기 메서드
    fun internalFileRead2(v:View) {
        var ifs : FileInputStream = openFileInput(fname) // 파일을 열기

        var buffer = ByteArray(30) // 파일의 데이터를 30바이트를 가져와 buffer 변수에 담기

        ifs.read(buffer)
        ifs.close()

        var contents = buffer.toString(Charsets.UTF_8) // 가져온 파일내용을 UTF-8로 변환
        Toast.makeText(applicationContext, "파일내용: ${contents}", Toast.LENGTH_SHORT).show()

        Log.d(TAG,"내부저장소에서 읽은 내용은 ${contents}")
    }
}

 

 

반응형

'JAVA &amp; APP :국비지원 학원 복습 > AndroidStudio' 카테고리의 다른 글

실습)동물사진 보기 앱  (0) 2021.02.25
02 안드로이드 : widget  (0) 2021.02.25
01 HelloAndroid  (0) 2021.02.24
반응형

체크박스를 체크 시 

좋아하는 동물을 묻는 TextView 위젯이 나오고 

아래에 radiobutton을 이용하여 동물 목록을 나오게 한다. 

특정 동물을 선택 - 선택완료 버튼 클릭 시

imageView를 이용하여 해당 동물 사진이 나오게 한다

 

 

 

empty activity를 생성해 위의 사진처럼 설정해준다. 

 

레이아웃 파일에 가서 위의 글대로 필요한 위젯들을 배치한다. ㅅ

 

 

클래스 파일에 가서 기능들을 구현해준다. 

package imlsw96.hellowidget

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.*

class AnimalPractice : AppCompatActivity() {

    lateinit var textV1 :TextView
    lateinit var startbtn: CheckBox
    lateinit var textV2: TextView
    lateinit var rGroup : RadioGroup
    lateinit var dogBtn : RadioButton
    lateinit var catBtn : RadioButton
    lateinit var rabbitBtn : RadioButton
    lateinit var choiceBtn : Button
    lateinit var animalPicture : ImageView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_animal_practice)
        title = "좋아하는 애완동물 고르기"

        initVar() // 변수 초기화 함수

        startbtn.setOnCheckedChangeListener() {CompoundButton, b->
            var state=if(startbtn.isChecked == true) View.VISIBLE else View.INVISIBLE
            textV2.visibility=state
            rGroup.visibility=state
            choiceBtn.visibility=state

        }

    }

    fun initVar() { // 변수 초기화 함수
        textV1 = findViewById(R.id.title)
        startbtn = findViewById(R.id.startbtn)
        textV2 = findViewById(R.id.title2)
        rGroup = findViewById(R.id.rGroup)
        dogBtn = findViewById(R.id.dogbtn)
        catBtn = findViewById(R.id.catbtn)
        rabbitBtn = findViewById(R.id.rabbitbtn)
        choiceBtn = findViewById(R.id.choicebtn)
        animalPicture = findViewById(R.id.imageview)
    }

    fun ShowAnimal(v: View) {
        animalPicture.visibility = View.VISIBLE
        when(rGroup.checkedRadioButtonId ){
            R.id.dogbtn -> animalPicture.setImageResource(R.drawable.dog)
            R.id.catbtn -> animalPicture.setImageResource(R.drawable.cat)
            R.id.rabbitbtn -> animalPicture.setImageResource(R.drawable.rabbit)
        }
    }
}

lateinit으로 나중에 초기화하는 변수를 선언하고 

 

변수 초기화를 해주는 함수 initVar() 함수를 만들어 정의한다. 

findViewById 메서드를 이용하여 layout 파일에 지정해준 View (위젯)들의 아이디 값을 변수에 저장해주고 

시작하기 체크박스 아래에 있는 모든 뷰들의 기본 visibility를 invisible로 설정해준다. 

 

이후 시작하기 체크박스에 클릭이벤트를 걸어 

체크가 되면 아래에 존재할 TEXTVIEW , RADIO GROUP 뷰들을 VISIBILE로 만들어주는 코드를 작성

 

이후 함수 ShowAnimal을 만들어 정의한다. 

정의하기 전 이 함수는 선택하기 버튼을 선택 시 발생하는 함수로 지정하기 위해

layout 파일에서 onClick 부분에 함수를 선택해준다.

 

정의 내용으로는 선택하기 버튼의 클릭이 발생하면 동물의 사진을 나타낼 imageView가 visible 해지고 

when 코틀린의 스위치 문? 을 이용하여 radio group 내부의 각 동물 버튼이 클릭될 때 

동물에 맞는 사진들의 소스로 imageVew를 변경되는 코드를 작성 

반응형

'JAVA &amp; APP :국비지원 학원 복습 > AndroidStudio' 카테고리의 다른 글

04 Files  (0) 2021.02.26
02 안드로이드 : widget  (0) 2021.02.25
01 HelloAndroid  (0) 2021.02.24
반응형

위젯 widget

안드로이드 화면(액티비티)을 구성하는 데 사용하는 요소. 

 

# View

View 클래스를 상속해서 만든 위젯들

 

# ViewGroup

ViewGroup. 클래스를 상속해서 만든 위젯들

뷰들을 적절한 위치에 배치하거나 여러 뷰들을 하나로 모으는데 사용

배치 관리자 : 뷰들을 적절한 위치에 배치 

컨테이너 : 여러 뷰들을 하나로 묶음

 

# TextView

사용자가 수정할 수 없는 텍스트를 표시하는 위젯

text

textSize

textColor

lines

 

# EditText

사용자에게 문자열이나 숫자를 입력받을 수 있는 위젯

input Type

layout_width

 

# Button

사용자가 손으로 클릭할 수 있는 위젯

버튼을 통해 사용자가 어떤 결정을 선택하게 하거나

다음 화면으로 전환하는 등의 인터랙티브 한 응답을 할 수 있음

backgroundTint : 버튼의 배경색

textAllCaps : 버튼의 영어 텍스트의 대문자 표시 여부

 

# checkbox

사용자가 여러 항목들 중 하나이상의 항목을 선택할 수 있게 해주는 위젯

checked :  gkdhardml tjsxorduqnfmf vytl

 

# radiobutton

사용자가 여러 항목들 중 하나의 항목만을 선택할 수 있게 해주는 위젯

checked : 항목의 선택여부를 표시

 

# togglebutton

사용자가 선택을 하면 커짐이 표시되고 다시 선택하면 꺼짐을 표시하는 위젯

textOff :  토글버튼이 꺼졌을 때 표시할 문자열 지정

textOn : 토글버튼이 켜졌을 때 표시할 문자열 지정

 

 

반응형

'JAVA &amp; APP :국비지원 학원 복습 > AndroidStudio' 카테고리의 다른 글

04 Files  (0) 2021.02.26
실습)동물사진 보기 앱  (0) 2021.02.25
01 HelloAndroid  (0) 2021.02.24
반응형

1. 톰캣서버를 가동하여 login / signup - signok 화면 띄우기 

- tiles 폴더에 페이지의 공통부분이 될 header / footer jsp파일 지정

 

#footer.jsp

<%@ page pageEncoding="UTF-8" %>
<footer class="row bg-light h-auto">
    <div class="col-7" style="margin:0 auto;">
        <hr>
        <div id="footermenu" style="height: 180px" class="row  mt-5">
            <div style="display:inline-block;"class="col-2">
                <p class="font-weight-bold">위시켓</p>
                <a href="#" class="small text-muted">위시켓 소개</a><br>
                <a href="#" class="small text-muted">신뢰와 안전</a><br>
                <a href="#" class="small text-muted">이용약관</a><br>
                <a href="#" class="small text-muted">개인정보 처리방침</a><br>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">고객센터 ></a></p>
                <a href="#" class="small text-muted">클라이언트 고객센터</a><br>
                <a href="#" class="small text-muted">파트너스 고객센터</a><br>
                <a href="#" class="small text-muted">이용요금</a><br>
                <a href="#" class="small text-muted">클라이언트 이용방법</a><br>
                <a href="#" class="small text-muted">파트너스 이용방법</a><br>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">뉴스센터 ></a></p>
                <a href="#" class="small text-muted">공지사항</a><br>
                <a href="#" class="small text-muted">위시켓 소식</a><br>
                <a href="#" class="small text-muted">위시켓 이용 팁</a><br>
                <a href="#" class="small text-muted">프로젝트 성공사례</a><br>
                <a href="#" class="small text-muted">언론 보도</a>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">이용후기 ></a></p>
                <p><a href="#" class="text-dark font-weight-bold">위시켓 스토어 ></a></p>
                <p><a href="#" class="text-dark font-weight-bold">요즘 IT ></a></p><br>
            </div>
            <div style="display:inline-block;"class="offset-1 col-3 ">
                <p class="font-weight-bold">CONTACT US</p>
                <a href="#" class="small text-muted">02-6925-4849(10:00-18:00, 공휴일 제외)</a><br>
                <a href="#" class="small text-muted">help@wishket.com</a><br>
            </div>
        </div><!-- 푸터메뉴 -->

        <hr>
        <div class="row" style="height: 200px">
            <div class="col-2 ">
                <img src="/img/wishket_footer_logo.png">
            </div>
            <div class="col-8">
                <p class="small text-muted">주식회사 위시켓 (대표이사: 박우범) / 서울특별시 강남구 테헤란로 211 한국고등교육재단빌딩 3층 (주)위시켓<br>
                    사업자등록번호: 209-81-57303 / 통신판매업신고: 제2018-서울강남-02337 호 / 직업정보제공사업 신고번호: J1200020180019</p>
                <p class="small text-muted">
                    © 2013 Wishket Corp.
                </p>
            </div>
            <div class="col-2">
                <a href="#"><img src="/img/footer_sns_facebook.png"></a>
                <a href="#"><img src="/img/footer_sns_blog.png"></a>
                <a href="#"><img src="/img/footer_sns_naver.png"></a>
            </div>
        </div>

    </div>
</footer>

# header.jsp

<%@ page pageEncoding="UTF-8" %>
<header class="row">
    <nav class="nav navbar-expand navbar-dark bg-dark col-12" >
        <ul class="nav navbar-nav  nav-fill w-100  col-7   " style="margin:15px auto;">
            <li class="nav-item small font-weight-bold"><a class="nav-link navbar-brand text-white" href="#">WishKet</a></li>
            <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">프로젝트 등록</a></li>
            <li class="nav-item small font-weight-bold align-self-center"><a  class="nav-link text-white "href="#">프로젝트 찾기</a></li>
            <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">파트너스 찾기</a></li>
            <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">이용방법</a></li>
            <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">이용후기</a></li>
            <span class="mt-2 text-white text-muted">|</span>
            <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">스토어</a></li>
            <li class="nav-item align-self-center" style="padding-left: 200px">
                <button type="button" class="btn btn-sm btn-light " id="loginbtn1">로그인</button>&nbsp&nbsp
                <button type="button" class="btn btn-sm btn-info " id="joinbtn">회원가입</button></li>
        </ul>
    </nav>
</header><!-- 헤더+네비 -->

 


2. 컨트롤러 클래스 / 서비스 클래스 / DAO 클래스 / VO 클래스 생성

위의 클래스를 만들어 톰캣의 기본 URL인 localhost:8080에 페이지를 띄우기 위해 

컨트롤러 클래스를 생성해 @GetMapping 어노테이션을 이용하여 

로그인페이지의 login.jsp 파일을 회원가입에 필요한 페이지인 signup / signok 페이지를 

servlet-context에 뷰리졸버를 타일즈로 설정해준거에 맞춰 코드를 작성해준다.

@Controller
public class SignController {

    @GetMapping("/accounts/login") // 로그인폼
    public String login() {return "accounts/login.tiles";}

   @GetMapping("/accounts/signup") // 회원가입 폼
    public String signup() {return "accounts/signup.tiles";}
   
   @GetMapping("/accounts/signok") // 회원가입 완료 부분
    public String signok() { return "accounts/signok.tiles";}
}

 

회원가입 singup.jsp 페이지에서 폼에 입력된 값들은 post 방식으로 DB에 값을 저장하기위해 

그에 필요한 VO / SERVICE / DAO 클래스 / sql문을 담아줄 (mybatis3) Mapper 파일을 생성하고 코드를 작성해주는데 

VO 클래스를 만들때는 테이블의 컬럼명과 동일하게 만들어준다. 

 

 

SERVICE / DAO 클래스의 코드를 작성하기 전에 대충 순서를 뇌피셜로 굴려보았을 때 

JSP 즉 회원가입페이지에서 input 태그 값들을 적고 submit input을 눌렀을때 

자바스크립트로 클릭이벤트를 걸고 이벤트 발생시 제이쿼리문으로 post method 하게 만든다. 

그러면 컨트롤러 클래스에 PostMapping을 걸어 요청을 받는 메서드를 생성하고 

메서드안에서 서비스 클래스 - DAO 클래스로 순차적으로 진행하여 DB에 값이 저장되게 진행하는데

 

서비스 클래스에서는 데이터가 잘저장이 되었는지 안되었는지 체크해주는 기능을 담당하고 

데이터에 직접 저장하는 역할은 DAO 클래스에서 Mapper파일을 이용해서 한다. 

 

그리고 추가적으로 자바스크립트와 DB의 저장된 값을 조회하여 

중복되는 아이디나 이메일이 저장되지 않도록 또한 비밀번호나 아이디 정규표현식 양식에 맞지

않으면 DB저장이 되지 않도록 다음 페이지로 진행이 되지 않도록 하는 코드들을 추가적으로 작성한다. 

 

input의 빈값 혹은 정규표현식에 맞지 않는 코드는 자바스크립트로 

 

DB 값을 조회해 ID / EMAIL 이 중복되지 않도록 하는 코드는 

@ResponseBody와 ajax를 비동기 방식을 이용한다.

ajax를 방식하는 원리를 뇌피셜로 굴려보면

1. @ResponseBody를 이용하고 임의의 url 을 설정하여 @GetMapping 을 이용하여 페이지를 만든다. 

2. 인자값으로 String email과 HttpServletResponse res 를 설정해준다. 

   설정해주는 이유로는 ajax를 이용하여 signup 페이지에서 중복체크하는 페이지로 

   중복체크할 데이터를 담아주기 위하여 email을 설정하고 

   쿼리문을 실행하여 데이터 존재유무를 중복체크 페이지에 기입해주기 위해 HttpServletResponse 객체를 이용한다.

3. Controller - Service - Dao 클래스를 거쳐

   데이터 존재유무에따라  0/1을 출력하는 페이지를 만들어 존재하지 않는다면 

   페이지가 안넘어가고 인풋에 반응형으로 빨간글씨가 나오게 설정 해주는 것.

 

# SignController 전체

package wishket.spring.mvc.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import wishket.spring.mvc.service.MemberService;
import wishket.spring.mvc.vo.MemberVO;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
public class SignController {
    @Autowired
    MemberService msrv;

    @GetMapping("/accounts/login") // 로그인폼
    public String login() {return "accounts/login.tiles";}

   @GetMapping("/accounts/signup") // 회원가입 폼
    public String signup() {return "accounts/signup.tiles";}

    @PostMapping("/accounts/signup") // 회원가입 처리
    public String signupok(MemberVO mvo, RedirectAttributes atb) {

        System.out.println(msrv.newMember(mvo));
        atb.addFlashAttribute("userid",mvo.getUserid());
        atb.addFlashAttribute("email",mvo.getEmail());
        return "redirect:/accounts/signok";
    }
    @ResponseBody // 아이디 중복체크
    @GetMapping("accounts/signup/checkUserid")
    public void checkUserid(String userid, HttpServletResponse res){
        try{
            res.getWriter().print(msrv.checkUserid(userid));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @ResponseBody // 이메일 중복체크
    @GetMapping("accounts/signup/checkEmail")
    public void checkEmail(String email, HttpServletResponse res){
        try{
            res.getWriter().print(msrv.checkEmail(email));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @GetMapping("/accounts/signok") // 회원가입 완료 부분
    public String signok() { return "accounts/signok.tiles";}
}

 

# Service 클래스 전체

package wishket.spring.mvc.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import wishket.spring.mvc.dao.MemberDAO;
import wishket.spring.mvc.vo.MemberVO;

@Service("msrv")
public class MemberServiceImpl implements MemberService{

    @Autowired
    private MemberDAO mdao;
    @Override
    public String newMember(MemberVO mvo) {
        String result="회원가입 실패!";
        int cnt=mdao.insertMember(mvo);
        if (cnt>0) result="회원가입 성공!";
        return result;
    }

    @Override
    public String checkUserid(String userid) {
        String isOk = "0";
        int  cnt = mdao.selectOneUserid(userid);
        if(cnt>0) isOk="1";
        return isOk;
    }

    @Override
    public String checkEmail(String email) {
        String isOk="0";
        int cnt = mdao.selectOneEmail(email);
        if(cnt>0) isOk="1";
        return isOk;
    }
}

# DAO 클래스 전체

package wishket.spring.mvc.dao;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import wishket.spring.mvc.vo.MemberVO;

@Repository("mdao")
public class MemberDAOImpl implements MemberDAO{

    @Autowired
    private SqlSession sqlSession;
    @Override
    public int insertMember(MemberVO mvo) {
        return sqlSession.insert("member.insertMember",mvo);
    }

    @Override
    public int selectOneUserid(String userid) {
        return sqlSession.selectOne("member.selectUserid",userid);
    }

    @Override
    public int selectOneEmail(String email) {
        return sqlSession.selectOne("member.selectEmail",email);
    }
}

# Mapper 파일 전체

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="member">
    <!-- 데이터입력 -->
    <insert id="insertMember" statementType="PREPARED" parameterType="wishket.spring.mvc.vo.MemberVO">
        insert into 회원 (userid, email, passwd) values (#{userid},#{email},#{passwd})
    </insert>
    <!-- 아이디 중복조회-->
    <select id="selectUserid" statementType="PREPARED" parameterType="String" resultType="int">
        select count(userid) cnt from 회원 where userid = #{userid}
    </select>
    <!-- 이메일 중복조회-->
    <select id="selectEmail" statementType="PREPARED" parameterType="String" resultType="int">
        select count(email) cnt from 회원 where email =#{email}
    </select>
</mapper>

# 자바스크립트 파일

// 회원가입 페이지 파트너스 / 클라이언트 선택하는 부분
let img1= document.getElementById('client')
let img2= document.getElementById('parteners')

img1.addEventListener('click',client)
img2.addEventListener('click',parteners)
function client() {
    if(img1.src="/img/signup_1.png")
        img1.src="/img/signup_2.png";
    img2.src="/img/signup_3.png";
}
function parteners() {
    if(img2.src="/img/signup_3.png")
        img2.src="/img/signup_4.png";
    img1.src="/img/signup_1.png";
}
// $('#idText').attr('style') == "color: red !important"
// 폼에 빈값들어가면 경고창 나오게
$('#signbtn1').on('click',function(){
    let msg1 = "사용불가능한이메일 입니다!"
    let msg2 = "사용불가아이디입니다!";
    if($('#email').val()=='' || $('#emailText').attr('style') == "color: red !important") alert("이메일을 확인해주세요");
    else if($('#userid').val()=='' || $('#idText').attr('style') == "color: red !important") alert("아이디를 확인해주세요");
    else if($('#passwd').val()=='' || $('#pwdText').attr('style') == "color: red !important") alert("비밀번호를 확인해주세요");
    else if($('#checkpwd').val()=='') alert("비밀번호를 한번 더 입력해주세요");
    else if($('#checkpwd').val() != $('#passwd').val()) alert("비밀번호가 서로 다릅니다.");
    else if(!$('#agree').is(':checked')) alert("개인정보 및 이용약관에 동의해주세요.");
    else {
        $('#accountfrm').attr("action","/accounts/signup");
        $('#accountfrm').attr("method", "post");
        $('#accountfrm').submit();
    }
});

// signup 페이지 메인 부분의 로그인 버튼
$("#loginbtn2").on('click',function() {location.href="/accounts/login"})


// 이메일 중복체크 +정규표현식
$('#email').on('blur', function(){ checkEmail(); });
$('#email').on('focus', function (){
    $('#emailText').text("비즈니스용 이메일 사용을 권장합니다");
    $('#emailText').attr('style', 'color: red !important');
});
function checkEmail() {
    let email = $('#email').val().trim();
    let reg = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
    if(email == ' '){
        $('#emailText').text("아이디는 8~16자 이내로 해주세요");
    }

    $.ajax({
        url:"/accounts/signup/checkEmail",type: 'GET',data:{email:$('#email').val()}
    }) // 비동기 요청
        .done(function (data){
            let msg = "사용불가능한 이메일 입니다 !"
            if (data.trim()=="0" && reg.test(email) ){
                msg = "사용가능한 이메일 입니다."
                $('#emailText').attr('style', 'color: blue !important');
            }
            $('#emailText').text(msg);
        })
        .fail(function (xhr, status, error) {
            alert(xhr.status, +"/" + error);
        }); // 비동기 요청 실패시
}

// 아이디 중복체크 + 정규표현식
$('#userid').on('blur', function(){ checkUserid(); });
$('#userid').on('focus', function (){
    $('#idText').text("아이디는 8~16자 이내로 해주세요");
    $('#idText').attr('style', 'color: red !important');
});

function checkUserid() {
    let userid = $('#userid').val();
    let reg = /^[0-9a-zA-Z]{8,16}$/
    if(userid.trim() == ' '){
        $('#idText').text("아이디는 8~16자 이내로 해주세요");
    }

    $.ajax({
        url: '/accounts/signup/checkUserid',
        type: 'GET', data: {userid: $('#userid').val()}
    }) //비동기 요청설정

        .done(function (data) {
            let msg = '사용 불가 아이디 입니다!';
            if (data.trim() == '0' && reg.test(userid)) {
                msg = '사용 가능 아이디 입니다.';
                $('#idText').attr('style', 'color: blue !important');
            }
            $('#idText').text(msg);
        }) // 비동기 요청 성공시

        .fail(function (xhr, status, error) {
            alert(xhr.status, +"/" + error);
        }); // 비동기 요청 실패시
}

// 비밀번호 정규표현식
$('#passwd').on('blur', function(){ checkPwd(); });
$('#passwd').on('focus', function (){
    $('#pwdText').text("비밀번호는 8자 이상 32자 이하로 대/소/숫자/특수문자 각각 한개 이상 입력해 주세요.");
    $('#pwdText').attr('style', 'color: red !important');
});
function checkPwd() {
    let pwd = $('#passwd').val();
    let reg =/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,32}$/
    if(pwd.trim() == ' ' || reg.test(pwd) != true) {
        $('#pwdText').text("사용 불가 비밀번호 입니다.");
    }else {
        $('#pwdText').text("사용 가능한 비밀번호 입니다.");
        $('#pwdText').attr('style', 'color: blue !important');
    }
}

// 비밀번호 다시쓰기 체크
$('#checkpwd').on('blur', function(){ recheckPwd(); });
$('#checkpwd').on('focus', function (){
    $('#checkpwdText').text("비밀번호를 한번 더 입력해주세요.");
    $('#checkpwdText').attr('style', 'color: red !important');
});

function recheckPwd() {
    if($('#passwd').val() == $('#checkpwd').val()){
        $('#checkpwdText').text("동일한 비밀번호 입니다.");
        $('#checkpwdText').attr('style', 'color: blue !important');
    }else $('#checkpwdText').text("동일한 비밀번호가 아닙니다.");
}

 

기능을 전부 캡쳐하기 어려워 중복확인기능 부분만 올림

테이블에 존재하는 데이터

중복이되어 밑에 text를 추가되는거 확인 가능

중복되지 않는 데이터는 그에 맞는 텍스트가 추가되도록 변경

반응형
반응형

# AndroidManifest.xml

안드로이드 앱의 기본정보를 저장한 파일

icon : 안드로이드 앱 아이콘 지정

label : 안드로이드 앱 제목 지정

activity : 안드로이드 앱 메인 액티비티 지정

android.intent.action.Main : 시작 액티비티 지정 여부

android.intent.category.LAUNCHER : 앱 실행 시 맨 처음 화면에 표시할 시작 액티비티로 지정

 


# activity_main.xml

화면에 보여질 위젯들을 배치하는 레이아웃 파일을 의미

GUI 형식으로 버튼이나 필요 위젯들을 직관적으로 배치할 수도 있고 

CODE형식으로 코드를 작성해서 배치나 설정등을 해줄 수 있다.


# MainActivity.kt

화면(액티비티)에 배치된 위젯에 이벤트를 추가해서 인터랙티브 한 작업을 하거나

기타 백엔드 작업을 위한 코드들을 추가하는 파일을 의미

 

# 액티비티 activity

앱을 실행했을때 화면을 구성할 수 있도록 해주는 안드로이드 요소

보통 앱의 종류에 따라 하나 이상의 액티비티로 구성될 수 있음

웹으로 비유하자면 하나의 웹페이지 개념으로 이해하면 됨 

 

# res 

앱 개발에 필요한 다양한 리소스들을 저장하는 디렉토리

drawable : 앱을 구성하는 각종 시각적 리소스들을 저장

layout : 앱 배치에 사용할 레이아웃 파일을 저장

values : 앱을 구성하는 각종 문자열을 저장하는 파일을 저장

 

# findViewById

레이아웃에 선언된 구성요소들을 액티비티에서 접근해서

조작하려면 해당 요소를 id로 지정해서 변수에 할당해야 함

findViewById 함수를 이용하면 해당 위젯을 선택한 다음 

변수에 할당할 수 있음

 

, 위젯을 지정할 때는 R.id. 위젯 아이디 형태로 사용해야 함 

 

 

# View 클래스

안드로이드 화면에서 실제로 사용되는 것들은 모두 View 클래스 상속을 받음

다른 말로 '위젯' 이라고도 함

# ApplicationContext  or ActivityContext

Context의 정의 

Application 환경에 대한 전역 정보를 접근하기 위한 인터페이스

추상 클래스이며 실제 구현은 Android 시스템에 의해 제공된다

Context를 통해 애플리케이션에 특화된 리소스나 클래스에 접근할 수 있다. 

Activity 실행, Intent 브로드캐스팅 그리고 Intent 수신 등과 같은 응용 프로그램 수준의 작업을 수행하기 위한 API를 

할 수 있다.

 

Context의 역할

Context는 애플리케이션과 관련된 정보에 접근하고자 하거나

어플리케이션과 연관된 시스템 레벨의 함수를 호출하고자 할 때 사용된다. 

그런데 안드로이드 시스템에서 어플리케이션 정보를 관리하고 있는 것은

시스템이 아닌, ActivityManagerService라는 일종의 또 다른 애플리케이션이다.

따라서 다른 일반적인 플랫폼과는 달리, 안드로이드에서는 애플리케이션과 관련된 정보에 접근하고자 할 때는 ActivityManagerService를 통해야만 한다. 

 

ApplicationContext

어플리케이션 자체와 연동되는 것이므로애플리케이션의 life cycle이 지속되는 동안 동일한 객체 

즉 애플리케이션을 종료 후 다시 실행시킬때에만 바뀌는 것. 

+ 어플리케이션 전연적으로 하나만 존재하는 싱글턴 객체 

 

ActivityContext

Activity의 라이프사이클과 함께 작동해 onDestroy()와 함께 사라진다.

즉, Activity에 대한 환경 정보들이 Context에 있고, 이 Context에 Intent를 통해 다른 액티비티를 띄우면

액티비티 스택이 쌓이게 된다.

 

# Intent

인텐트는 앱 컴포넌트(Component)가 무엇을 할 것인지를 담는 메시지 객체이다.

메시지는 의사소통을 하기 위해 보내고 받는 것

메시지를 사용하는 가장 큰 목적은 

다른 액티비티, 서비스 , 브로드캐스트 , 리시버 , 컨텐트 프로바이더 등을 실행하는 것.

인텐트는 이들 사이에서 데이터를 주고받기 위한 용도로 사용된다.

 

intent 사용규칙

1. var로 선언한 프로퍼티에만 사용할 수 있다.

2. 클래스 몸체에 선언한 프로퍼티에만 사용할 수 있다. 주 생성자에는 사용할 수 없다.

3. 사용자 정의 getter/setter를 사용하지 않은 프로퍼티에만 사용할 수 있다.

4. null 허용프로 퍼티에는 사용할 수 없다. 

5. 기초 타입 프로퍼티에는 사용할 수 없다.

 

애플리케이션을 만들때 하나의 화면을 가지고 어플리케이션을 만들지는 않는다. 

하나의 화면에 모든 기능을 담기란 불가능하다.

대부분의 애플리케이션은 많은 View를 모으고 모아서 만든 결과물이다. 

그래서 안드로이드에서 화면 간 이동과 화면간 데이터 전달이 무척이나 빈 전하고 중요하다.

안드로이드에서 페이지 전환과 페이지간 데이터 전달은 Intent를 통해서 구현이 가능하다.

 


실습)

1. HelloAndroid라는 액티비티를 생성 

2. "Hello World" 문구를 보여주는 TextView 위젯 생성

3. 5개 버튼을 생성해서 각 버튼에 ID를 지정 / onClick 이벤트 발생 시 실행될 함수를

4. MainActivity.kt파일 안에서 정의  + 필요한 버튼 ID를 FindViewById를 이용해 변수 설정

 

 

package imlsw96.helloandroid

import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast

class MainActivity : AppCompatActivity() {

    // 변수선언시 초기화를 바로 못하는 경우 lateinit 키워드 사용
    lateinit var btnSayHello : Button
    lateinit var btnGoogle : Button
    //lateinit var btn911 : Button
    //lateinit var btnGallery : Button
    //lateinit var btnFinish : Button

    override fun onCreate(savedInstanceState: Bundle?) {
        // 안드로이드 앱의 실행 진입점
        // 즉, 현재 액티비티가 생성되면 뭔가를 실행하라는 의미

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 화면에 무언가를 출력함
        // res - layout - activity_main.xml

        btnSayHello = findViewById(R.id.btnSayHello)
        btnGoogle = findViewById(R.id.btnGoogle)
        // activity_main.xml에 만들어 놓은 id로 위젯을 찾아서 변수에 할당
        // btn911 = findViewById(R.id.btn911)
        //btnGallery = findViewById(R.id.btnGallery)
        //btnFinish = findViewById(R.id.btnFinish)

        btnSayHello.setOnClickListener() {  // btnSayHello에 클릭이벤트 추가
            Toast.makeText(applicationContext, "Hello, World!!",
            Toast.LENGTH_SHORT).show()
            // makeText(대상, 메세지, 지연시간).show() 잠시동안 메세지를 출력함
            // 코틀린에서의 Toast는 자바스크립트의 alert창과 유사하다.
        }

        btnGoogle.setOnClickListener() {
            var gIntent : Intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.co.kr"))
            startActivity(gIntent)
        } // 버튼 클릭시 새로운 브라우져 뷰를 열어서 구글 홈페이지를 띄움
    }

    // 특정 위젯을 findViewById 함수로 잡아서 이벤트를 추가할때마다 코드가 지저분해짐
    // 따라서, 이벤트 처리를 위한 독립적인 함수를 만들어 처리하는 것이 좋음
    // 레이아웃의 onClick 속성에 이벤트 처리함수를 지정하면 됨
    fun call911(v: View){
        var mIntent : Intent = Intent(Intent.ACTION_VIEW, Uri.parse("tel:/911"));
        startActivity(mIntent)
    }

    fun openGallery(v: View) {
        var mIntent : Intent = Intent(Intent.ACTION_VIEW, Uri.parse("content://media/internal/images/media"));
        startActivity(mIntent)
    }
    fun appFinish(v: View) {
        finish();
    }
}

lateinit 으로 변수를 클래스 안에서 설정해준것을 확인할 수 있다. '

lateinit은 코드 설명을 보면 알 수 있듯이 변수 선언시 초기화를 바로 못하는 경우 사용한다고 나와있다.

여기서 든 의문점으로는 초기화를 바로 못하는 경우는 언제인가에 대한 의문점이 생겼다. 

뇌피셜을 돌려보려 코드를 찬찬히 보니 메서드 재정의한 onCreate부분을 봐보면 앱의 액티비티 실행 진입점임을

확인할 수 있는데 lateinit으로 선언한 변수들은 버튼이 클릭되었을때 이벤트를 걸기위해 설정을 해둔것이 아닌가

 

 

Toast 하는 문장에서 대상 부분에 applicationContext라고 지정되어있는데 이 부분은 지금 현재 MainActivity를 위치에 해당하는 의미 

 

위의 설명에서 뷰 클래스는 액티비티의 '위젯'들의 최상위 클래스이다. 그중 버튼에 onClick에 맞는 함수들을 걸어놨으니 함수의 인자 값은 View 클래스가 지정된다. 

 

 

반응형

'JAVA &amp; APP :국비지원 학원 복습 > AndroidStudio' 카테고리의 다른 글

04 Files  (0) 2021.02.26
실습)동물사진 보기 앱  (0) 2021.02.25
02 안드로이드 : widget  (0) 2021.02.25
반응형

나는 팀에서 첫번째로 비교적 쉬운 로그인 / 회원가입 페이지를 짜보기로 하였다. 

작업은 학원수업에서 springMVC 수업에 진행했던거랑 동일한 프로젝트 구조로 만들었다. 

ex) Maven으로 spring프로젝트를 생성하고 추가적으로 mybatis와 tiles, bootstrap을 이용하여서 만들었다. 

 

 

작업의 첫번째로는 html / 부트스트랩을 이용해서 웹페이지의 기본골격을 짜는 것이다. 

 

// 로그인화면

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="../../../resources/css/bootstrap.min.css" >
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
    <title>로그인</title>
</head>
<body>
<div class="container-fluid">
    <!-- 헤더+네비 -->
    <header class="row">
        <nav class="nav navbar-expand navbar-dark bg-dark col-12" >
            <ul class="nav navbar-nav  nav-fill w-50  align-self-center   col-7  " style="margin:15px auto;">
                <li class="nav-item"><a class="nav-link navbar-brand text-white" href="#">WishKet</a></li>
                <li class="nav-item"><a class="nav-link text-white" href="#">프로젝트 등록</a></li>
                <li class="nav-item"><a  class="nav-link text-white "href="#">프로젝트 찾기</a></li>
                <li class="nav-item"><a class="nav-link text-white" href="#">파트너스 찾기</a></li>
                <li class="nav-item"><a class="nav-link text-white" href="#">이용방법</a></li>
                <li class="nav-item"><a class="nav-link text-white" href="#">이용후기</a></li>
                <span class="mt-2 text-white text-muted">|</span>
                <li class="nav-item "><a class="nav-link text-white" href="#">스토어</a></li>
                <li class="nav-item"><button type="button" class="btn btn-light" id="loginbtn1">로그인</button>&nbsp&nbsp
                    <button type="button" class="btn btn-info" id="joinbtn">회원가입</button></li>
            </ul>
        </nav>
    </header><!-- 헤더+네비 -->

    <!-- 메인 -->
    <div id="main" class="row bg-light" style="height: 680px">

        <div id="intro" class="border col-7 mt-4 bg-white" style="height: 116px; margin:0 auto;">
            <div style="margin:25px 20px">
                <h4 class="font-weight-bold text-dark">로그인</h4>
                <p class="text-muted">위시켓에 오신 것을 환영합니다.</p>
            </div>
            </div>

        <div id="input" class="card card-body bg-white  col-7 border mt-3 row" style="height: 308px; margin:320px auto;" >
            <div class="col-7 offset-1 mt-3 " style="display:inline-block;">
                <div class="form-group row ">
                <label for="userid" class="col-form-label col-4 text-right font-weight-bold "><span class="text-danger">*</span>아이디 또는 이메일</label>
                <input type="text" id="userid" class="form-control col-6">
                </div>
                <div class="form-group row">
                    <label for="pwd" class="col-form-label col-4 text-right font-weight-bold"><span class="text-danger">*</span>비밀번호</label>
                    <input type="password" id="pwd" class="form-control col-6 " >
                </div>
                <div class="form-group row">
                    <div class="custom-control custom-checkbox offset-4 col-6">
                    <input type="checkbox" id="keep" class="custom-control-input">
                    <label for="keep" class="text-muted custom-control-label text-dark">로그인상태 유지</label>
                    </div>
                </div>
                <div class="form-group row">
                    <button type="button" id="loginbtn2" class="btn btn-info offset-4 col-6">로그인</button>
                    <small class="text-muted offset-3 col-7 mt-2  pl-5">비밀번호를 잊으셨나요? <a href="#" class="font-weight-bold text-info">비밀번호 찾기</a></small>
                </div>
            </div>

            <div class="col-4 border-left mt-3" style="display:inline-block; padding-bottom:58px">
                <p class="font-weight-bold">이미 페이스북으로 가입하셨다면</p>
                <button type="button" class="btn" style="margin-left:-10px"><img src="../../../resources/img/login_facebook.png"></button>
                <hr style="border-style: dashed; width: 280px; margin-left: -5px">
                <p>아직 회원이 아니신가요? <a  href="#" class="font-weight-bold text-info">회원가입하기</a></p>
            </div>
        </div>


    </div><!-- 메인 -->

    <footer class="row bg-light h-auto">
        <div class="col-7" style="margin:0 auto;">
            <hr>
            <div id="footermenu" style="height: 180px" class="row  mt-5">
            <div style="display:inline-block;"class="col-2">
                <p class="font-weight-bold">위시켓</p>
                <a href="#" class="small text-muted">위시켓 소개</a><br>
                <a href="#" class="small text-muted">신뢰와 안전</a><br>
                <a href="#" class="small text-muted">이용약관</a><br>
                <a href="#" class="small text-muted">개인정보 처리방침</a><br>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">고객센터 ></a></p>
                <a href="#" class="small text-muted">클라이언트 고객센터</a><br>
                <a href="#" class="small text-muted">파트너스 고객센터</a><br>
                <a href="#" class="small text-muted">이용요금</a><br>
                <a href="#" class="small text-muted">클라이언트 이용방법</a><br>
                <a href="#" class="small text-muted">파트너스 이용방법</a><br>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">뉴스센터 ></a></p>
                <a href="#" class="small text-muted">공지사항</a><br>
                <a href="#" class="small text-muted">위시켓 소식</a><br>
                <a href="#" class="small text-muted">위시켓 이용 팁</a><br>
                <a href="#" class="small text-muted">프로젝트 성공사례</a><br>
                <a href="#" class="small text-muted">언론 보도</a>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">이용후기 ></a></p>
                <p><a href="#" class="text-dark font-weight-bold">위시켓 스토어 ></a></p>
                <p><a href="#" class="text-dark font-weight-bold">요즘 IT ></a></p><br>
            </div>
                <div style="display:inline-block;"class="offset-1 col-3 ">
            <p class="font-weight-bold">CONTACT US</p>
            <a href="#" class="small text-muted">02-6925-4849(10:00-18:00, 공휴일 제외)</a><br>
            <a href="#" class="small text-muted">help@wishket.com</a><br>
        </div>
            </div><!-- 푸터메뉴 -->

            <hr>
            <div class="row" style="height: 200px">
                <div class="col-2 ">
                    <img src="../../../resources/img/wishket_footer_logo.png">
                </div>
                <div class="col-8">
                    <p class="small text-muted">주식회사 위시켓 (대표이사: 박우범) / 서울특별시 강남구 테헤란로 211 한국고등교육재단빌딩 3층 (주)위시켓<br>
                        사업자등록번호: 209-81-57303 / 통신판매업신고: 제2018-서울강남-02337 호 / 직업정보제공사업 신고번호: J1200020180019</p>
                    <p class="small text-muted">
                        © 2013 Wishket Corp.
                    </p>
                </div>
                <div class="col-2">
                    <a href="#"><img src="../../../resources/img/footer_sns_facebook.png"></a>
                    <a href="#"><img src="../../resources/img//footer_sns_blog.png"></a>
                    <a href="#"><img src="../../../resources/img/footer_sns_naver.png"></a>
                </div>
            </div>

        </div>
    </footer>
</div>




<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="../../../resources/js/bootstrap.bundle.min.js"></script>
</body>
</html>

// 회원가입 화면

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="../../../resources/css/bootstrap.min.css" >
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
    <title>회원가입</title>

    <style>
        .bidragup {position:relative; top:-1px}
    </style>
</head>
<body>
<div class="container-fluid">
    <!-- 헤더+네비 -->
    <header class="row">
        <nav class="nav navbar-expand navbar-dark bg-dark col-12" >
            <ul class="nav navbar-nav  nav-fill w-100  col-7   " style="margin:15px auto;">
                <li class="nav-item small font-weight-bold"><a class="nav-link navbar-brand text-white" href="#">WishKet</a></li>
                <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">프로젝트 등록</a></li>
                <li class="nav-item small font-weight-bold align-self-center"><a  class="nav-link text-white "href="#">프로젝트 찾기</a></li>
                <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">파트너스 찾기</a></li>
                <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">이용방법</a></li>
                <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">이용후기</a></li>
                <span class="mt-2 text-white text-muted">|</span>
                <li class="nav-item small font-weight-bold align-self-center"><a class="nav-link text-white" href="#">스토어</a></li>
                <li class="nav-item align-self-center" style="padding-left: 200px">
                    <button type="button" class="btn btn-sm btn-light " id="loginbtn1">로그인</button>&nbsp&nbsp
                    <button type="button" class="btn btn-sm btn-info " id="joinbtn">회원가입</button></li>
            </ul>
        </nav>
    </header><!-- 헤더+네비 -->

    <!-- 메인 -->
    <div id="main" class="row bg-light" style="height: auto">

        <div id="intro" class="border col-7 mt-4 bg-white" style="height: 116px; margin:0 auto;">
            <div style="margin:25px 20px">
                <h4 class="font-weight-bold text-dark">회원가입</h4>
                <p class="text-muted">위시켓에 오신 것을 환영합니다.</p>
            </div>
        </div>

        <div id="input" class="card card-body bg-white  col-7 mt-3 row" style="height:780px; margin:50px auto;" >
            <div class="col-7 offset-1 mt-3 " style="display:inline-block;">
                <div class="form-group row">
                    <label class="col-form-label col-2 text-right font-weight-bold"><span class="text-danger">*</span>이용목적</label>
                    <img src="../../../resources/img/signup_1.png" id="client">
                    <img src="../../../resources/img/signup_3.png" id="parteners" class="ml-3">
                    <small class="text-muted  offset-2 mt-2">선택한 이용목적에 따라 서비스 이용에 차이가 발생합니다</small>
                </div>

                <div class="form-group row ">
                    <label for="email" class="col-form-label col-2 text-right font-weight-bold "><span class="text-danger">*</span>이메일</label>
                    <input type="text" id="email" class="form-control col-8">
                    <small class="text-muted  offset-2 mt-2">비즈니스용 이메일 사용을 권장합니다 <i class="bi bi-question-circle bidragup"></i></small>
                </div>

                <div class="form-group row ">
                    <label for="email" class="col-form-label col-2 text-right font-weight-bold "><span class="text-danger">*</span>아이디</label>
                    <input type="text" id="userid" class="form-control col-8">
                </div>

                <div class="form-group row">
                    <label for="pwd" class="col-form-label col-2 text-right font-weight-bold"><span class="text-danger">*</span>비밀번호</label>
                    <input type="password" id="pwd" class="form-control col-8" >
                    <small class="text-muted  offset-2 mt-2">비밀번호는 8자 이상 32자 이하로 입력해 주세요.<i class="bi bi-question-circle bidragup"></i></small>
                </div>

                <div class="form-group row ">
                    <label for="checkpwd" class="col-form-label col-2  font-weight-bold" style="width: 200px"><span class="text-danger">*</span>비밀번호</label>
                    <input type="password" id="checkpwd" class="form-control col-8">
                    <small class="text-muted  offset-2 mt-2">동일한 비밀번호를 입력해 주세요.<i class="bi bi-question-circle bidragup"></i></small>
                </div>

                <div class="form-group row">
                    <div class="custom-control custom-checkbox offset-2 col-8">
                        <input type="checkbox" id="keep" class="custom-control-input">
                        <label for="keep" class="text-muted custom-control-label text-dark"><a href="#" class="text-info">이용약관</a> 및 <a href="#" class="text-info">개인정보 처리방침</a>에 동의합니다.</label>
                    </div>
                </div>

                <div class="form-group row">
                    <button type="button" id="signbtn1" class="btn btn-info offset-2 col-8">회원가입</button>
                </div>
            </div>

            <div class=" col-5 border-left mt-3 " style="display:inline-block; padding-bottom:58px">
                <p class="font-weight-bold ml-4">페이스북 계정이 있으신가요?</p>
                <button type="button" class="btn ml-3" style="margin-left:-10px"><img src="../../../resources/img/login_facebook.png"></button>
                <hr style="position: relative; left:-48px; width: 350px">
                <p class="font-weight-bold ml-3 pt-1 pb-1">이미 회원이신가요 ?</p>
                <div class="button btn border-info text-info col-sm-8 ml-3" id="loginbtn2">로그인</div>
                <hr style="position: relative; left:-48px; width: 350px">
                <p class="small ml-3 pt-3"><span class="font-weight-bold ">71,321</span>개의 클라이언트 기업과<br>
                    <span class="font-weight-bold ">86,354</span>개의 개발회사 & 프리랜서가<br>
                    <span class="font-weight-bold ">함께하는 온라인 아웃소싱 플랫폼 위시켓</span></p>
                <img src="../../../resources/img/introduction-img.png" class="w-50 ml-3">
            </div>
        </div>

        <div style="margin-bottom:350px"></div>
    </div><!-- 메인 -->

    <footer class="bg-light row" >
        <div class=" col-7 bg-light" style="margin:0px auto;">
            <hr>
            <div id="footermenu" style="height: 180px" class="row  mt-5">
                <div style="display:inline-block;"class="col-2">
                    <p class="font-weight-bold">위시켓</p>
                    <a href="#" class="small text-muted">위시켓 소개</a><br>
                    <a href="#" class="small text-muted">신뢰와 안전</a><br>
                    <a href="#" class="small text-muted">이용약관</a><br>
                    <a href="#" class="small text-muted">개인정보 처리방침</a><br>
                </div>
                <div style="display:inline-block;" class="col-2">
                    <p><a href="#" class="text-dark font-weight-bold">고객센터 ></a></p>
                    <a href="#" class="small text-muted">클라이언트 고객센터</a><br>
                    <a href="#" class="small text-muted">파트너스 고객센터</a><br>
                    <a href="#" class="small text-muted">이용요금</a><br>
                    <a href="#" class="small text-muted">클라이언트 이용방법</a><br>
                    <a href="#" class="small text-muted">파트너스 이용방법</a><br>
                </div>
                <div style="display:inline-block;" class="col-2">
                    <p><a href="#" class="text-dark font-weight-bold">뉴스센터 ></a></p>
                    <a href="#" class="small text-muted">공지사항</a><br>
                    <a href="#" class="small text-muted">위시켓 소식</a><br>
                    <a href="#" class="small text-muted">위시켓 이용 팁</a><br>
                    <a href="#" class="small text-muted">프로젝트 성공사례</a><br>
                    <a href="#" class="small text-muted">언론 보도</a>
                </div>
                <div style="display:inline-block;" class="col-2">
                    <p><a href="#" class="text-dark font-weight-bold">이용후기 ></a></p>
                    <p><a href="#" class="text-dark font-weight-bold">위시켓 스토어 ></a></p>
                    <p><a href="#" class="text-dark font-weight-bold">요즘 IT ></a></p><br>
                </div>
                <div style="display:inline-block;"class="offset-1 col-3 ">
                    <p class="font-weight-bold">CONTACT US</p>
                    <a href="#" class="small text-muted">02-6925-4849(10:00-18:00, 공휴일 제외)</a><br>
                    <a href="#" class="small text-muted">help@wishket.com</a><br>
                </div>
            </div><!-- 푸터메뉴 -->

            <hr>
            <div class="row pt-2" style="height: 100px">
                <div class="col-2 ">
                    <img src="../../../resources/img/wishket_footer_logo.png">
                </div>
                <div class="col-8">
                    <p class="small text-muted">주식회사 위시켓 (대표이사: 박우범) / 서울특별시 강남구 테헤란로 211 한국고등교육재단빌딩 3층 (주)위시켓<br>
                        사업자등록번호: 209-81-57303 / 통신판매업신고: 제2018-서울강남-02337 호 / 직업정보제공사업 신고번호: J1200020180019</p>
                    <p class="small text-muted">
                        © 2013 Wishket Corp.
                    </p>
                </div>
                <div class="col-2">
                    <a href="#"><img src="../../../resources/img/footer_sns_facebook.png"></a>
                    <a href="#"><img src="../../resources/img//footer_sns_blog.png"></a>
                    <a href="#"><img src="../../../resources/img/footer_sns_naver.png"></a>
                </div>
            </div>

        </div>
    </footer>
</div>




<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="../../../resources/js/bootstrap.bundle.min.js"></script>
<script>



</script>
</body>
</html>

// 회원가입 완료 화면 

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="../../../resources/css/bootstrap.min.css" >
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
    <title>로그인</title>
</head>
<body>
<div class="container-fluid">
    <!-- 헤더+네비 -->
    <header class="row">
        <nav class="nav navbar-expand navbar-dark bg-dark col-12" >
            <ul class="nav navbar-nav  nav-fill w-50  align-self-center   col-7  " style="margin:15px auto;">
                <li class="nav-item"><a class="nav-link navbar-brand text-white" href="#">WishKet</a></li>
                <li class="nav-item"><a class="nav-link text-white" href="#">프로젝트 등록</a></li>
                <li class="nav-item"><a  class="nav-link text-white "href="#">프로젝트 찾기</a></li>
                <li class="nav-item"><a class="nav-link text-white" href="#">파트너스 찾기</a></li>
                <li class="nav-item"><a class="nav-link text-white" href="#">이용방법</a></li>
                <li class="nav-item"><a class="nav-link text-white" href="#">이용후기</a></li>
                <span class="mt-2 text-white text-muted">|</span>
                <li class="nav-item "><a class="nav-link text-white" href="#">스토어</a></li>
                <li class="nav-item"><button type="button" class="btn btn-light" id="loginbtn1">로그인</button>&nbsp&nbsp
                    <button type="button" class="btn btn-info" id="joinbtn">회원가입</button></li>
            </ul>
        </nav>
    </header><!-- 헤더+네비 -->

    <!-- 메인 -->
    <div id="main" class="row bg-light" style="height: 680px">

        <div id="intro" class="border col-7 mt-4 bg-white" style="height: 116px; margin:0 auto;">
            <div style="margin:25px 20px">
                <h4 class="font-weight-bold text-dark">회원가입 요청 완료</h4>
                <p class="text-muted">위시켓 이용을 위해서는 인증 메일 확인이 필요합니다.</p>
            </div>
            </div>

        <div id="signok" class="card card-body bg-white  col-7 mt-3" style="height: 400px; margin:320px auto;" >
            <div class="form-group row mt-4">
                <div class="offset-1 col-7 ">
            <p >${userid}님 <br>
            가입하신 이메일 주소 <br>
            <span style="font-weight: bold">${email}</span>로 인증메일 보내드렸습니다.<br>
            이메일 인증 한 후에 정상적인 서비스 이용이 가능합니다.<br><br>
            혹시 인증메일을 못받으셨나요 ?
            </p>
                    <button class="btn btn-info">인증메일 다시 받기</button>
                </div>
                <div class="col-4">
            <img src="/img/use-icon-contrat-c-3.png">
                </div>
                </div>

            <div class="form-group row ">
                <div class="offset-1 col-10 offset-1">
                    <hr>
                    <p>
                        1. 7일 이내에 이메일의 인증 링크를 클릭해 주시면 가입이 승인됩니다.<br>
                        2. 인증 메일이 스팸함으로 가는 경우도 있으니 확인해 주세요.
                    </p>
                </div>
            </div>

        </div>



    </div><!-- 메인 -->

    <footer class="row bg-light h-auto">
        <div class="col-7" style="margin:0 auto;">
            <hr>
            <div id="footermenu" style="height: 180px" class="row  mt-5">
            <div style="display:inline-block;"class="col-2">
                <p class="font-weight-bold">위시켓</p>
                <a href="#" class="small text-muted">위시켓 소개</a><br>
                <a href="#" class="small text-muted">신뢰와 안전</a><br>
                <a href="#" class="small text-muted">이용약관</a><br>
                <a href="#" class="small text-muted">개인정보 처리방침</a><br>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">고객센터 ></a></p>
                <a href="#" class="small text-muted">클라이언트 고객센터</a><br>
                <a href="#" class="small text-muted">파트너스 고객센터</a><br>
                <a href="#" class="small text-muted">이용요금</a><br>
                <a href="#" class="small text-muted">클라이언트 이용방법</a><br>
                <a href="#" class="small text-muted">파트너스 이용방법</a><br>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">뉴스센터 ></a></p>
                <a href="#" class="small text-muted">공지사항</a><br>
                <a href="#" class="small text-muted">위시켓 소식</a><br>
                <a href="#" class="small text-muted">위시켓 이용 팁</a><br>
                <a href="#" class="small text-muted">프로젝트 성공사례</a><br>
                <a href="#" class="small text-muted">언론 보도</a>
            </div>
            <div style="display:inline-block;" class="col-2">
                <p><a href="#" class="text-dark font-weight-bold">이용후기 ></a></p>
                <p><a href="#" class="text-dark font-weight-bold">위시켓 스토어 ></a></p>
                <p><a href="#" class="text-dark font-weight-bold">요즘 IT ></a></p><br>
            </div>
                <div style="display:inline-block;"class="offset-1 col-3 ">
            <p class="font-weight-bold">CONTACT US</p>
            <a href="#" class="small text-muted">02-6925-4849(10:00-18:00, 공휴일 제외)</a><br>
            <a href="#" class="small text-muted">help@wishket.com</a><br>
        </div>
            </div><!-- 푸터메뉴 -->

            <hr>
            <div class="row" style="height: 200px">
                <div class="col-2 ">
                    <img src="../../../resources/img/wishket_footer_logo.png">
                </div>
                <div class="col-8">
                    <p class="small text-muted">주식회사 위시켓 (대표이사: 박우범) / 서울특별시 강남구 테헤란로 211 한국고등교육재단빌딩 3층 (주)위시켓<br>
                        사업자등록번호: 209-81-57303 / 통신판매업신고: 제2018-서울강남-02337 호 / 직업정보제공사업 신고번호: J1200020180019</p>
                    <p class="small text-muted">
                        © 2013 Wishket Corp.
                    </p>
                </div>
                <div class="col-2">
                    <a href="#"><img src="../../../resources/img/footer_sns_facebook.png"></a>
                    <a href="#"><img src="../../resources/img//footer_sns_blog.png"></a>
                    <a href="#"><img src="../../../resources/img/footer_sns_naver.png"></a>
                </div>
            </div>

        </div>
    </footer>
</div>




<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="../../../resources/js/bootstrap.bundle.min.js"></script>
</body>
</html>

 

웹페이지 골격은 그냥 원본 웹페이지를 계속 뚫어져라 처다보고 코드를 써내려가고 수정하고 다시 써내려가고 하면

얼추 비슷하게 만들어지는거 같아서 큰 어려움을 느끼진 못했는데 창의 화면을 줄이거나 하면 웹페이지의 틀이 찌그러지던데 이부분은 추가적으로 알아보고 수정해야 할 부분같다. 

반응형
반응형

list

자바와는 다르게 list에는 읽기전용과 수정가능한 객체등 2가지가 각각 지원

리스트 객체를 만들때는 listOf라는 함수를 사용함

읽기전용 리스트 객체를 만들때는 listOf라는 함수를 사용함

쓰기가능 리스트 객체를 만들때는 mutableListOf라는 함수를 사용함

 

filterNotNull이라는 함수를 사용하면 null을 제외한 나머지 요소만 다룰수 있음

+,- 연산자를 이용해서 특정 요소를 추가하거나 제거할 수 있음

fun main() {
	
    println("-읽기전용 리스트-")
    val names = listOf("아이린","수지","아이유")
    for(i in 0..names.size-1) println(names[i])
    
    println("-쓰기전용-")
    val names2 = mutableListOf("짜장면","탕수육","피자")
    names2.add("치킨") // 리스트에 "치킨" 추가
    for(i in 0..names2.size-1) println(names2.get(i))
}

package practice

fun main() {

    println("-쓰기전용-")
    val names2 = mutableListOf("짜장면","탕수육","피자")
    names2.add("치킨") // 리스트에 "치킨" 추가
    for(i in 0..names2.size-1) println(names2.get(i))

    val names2B = names2 - "짜장면" + null //짜장면을 지우고 null 값 추가
    for(i in 0..names2B.size-1) println(names2B.get(i))

    println("-null값 제외하기-")
    for(str in names2B.filterNotNull()) println(str)
}


map

자바와는 다르게 map에는 읽기전용과 수정가능한 객체등 2가지가 각각 지원

읽기전용 리스트 객체를 만들때는 mapOf라는 함수를 사용함

쓰기기능 리스트 객체를 만들대는 mutablMapOf라는 함수를 사용함

+,- 연산자를 이용해서 특정요소를 추가하거나 제거할 수 있는데 

제거시 키를 지정해야 함. 

 

fun main() {
	val names = mutableMapOf(1 to "제니", 2 to "조이", 3 to "지수")
    names[4]="수지" 		// 키값 4의 value 수지 설정
    names.put(5,"다현") // 키값  5의 value 다현 설정
    names[3] = "로제" // 키값 3의 value "지수" -> "로제" 수정
    
    for (name in names) println("${name.key}, ${name.value}")

}

package practice

fun main() {
    val names = mutableMapOf(1 to "제니", 2 to "조이", 3 to "지수")
    names[4]="수지" 		// 키값 4의 value 수지 설정
    names.put(5,"다현") // 키값  5의 value 다현 설정
    names[3] = "로제" // 키값 3의 value "지수" -> "로제" 수정

	val names2 = names -3 + (6 to "아이유") // 키값 3의 value "로제" 제거 + 
    										// 키값 6의 value "아이유" 추가
   
    for (name in names2) println("${name.key}, ${name.value}")

}

 

반응형
반응형

학원에서 팀원들을 임의로 정해서 팀 프로젝트를 시작하였다. 

팀 프로젝트 내용으로는 흔히 우리가 알고 있는 웹을 그대로 만들거나 

팀원들과 상의를 통해 원하는 웹페이지를 만들고

목적에 맞게 웹페이지의 기능을 구현하는 프로젝트이다. 

 

우리 조는 위시캣이라는 웹페이지를 만들어 보기로 했다. 

www.wishket.com/

 

위시켓! IT 아웃소싱을 빠르고 안전하게

위시켓은 기업의 프로젝트와 IT프리랜서를 이어주는 온라인 아웃소싱 플랫폼입니다. 위시켓만의 지원 시스템을 통해 빠른 모집이 가능해집니다.

www.wishket.com

팀원분의 추천으로 웹페이지를 살펴보았는데 위의 링크에 설명에 나와있듯이 

기업이나 개인이 프로젝트를 의뢰하고 프로그래머들은 그에 맞는 프로젝트를 찾아 신청을 하고 

참여해서 수당을 받아가는 구조인 거로 파악을 했는데 

프로그래밍에 대해서는 이번에 학원을 다니면서 접하다 보니 이런 사이트도 있구나 하면서 생소하면서 신기했다. 

 

이 웹페이지를 보았을때 핵심적으로 기능을 구현해야 할 부분은

프로젝트 찾기페이지의 왼쪽 사이드바에 프로젝트들을 사용자의 요구에 맞게 세분화하여 체크하고 

체크에 맞는 프로젝트들이 오른쪽 메인에 글 형태로 나오게 페이징을 잘하는 것이 핵심이라 생각된다. 

 

이 페이징하는 부분은 학원 수업에서 간단하게 게시판에서 글을 쓰고 글을 쪼개 

한 페이지에 몇 개의 글을 보여줄 것인가 정도로만 짜 보아서 위 페이지의 기능을 구현하는 게 

가장 어렵게 다가온다 . 

 

일단은 코틀린이랑 안드로이드 스튜디오 수업의 진도가 남아있어서 

본격적인 프로젝트는 시작안해서 역할분담이 제대로 되진 않았지만 

짬짬이 남는 시간에 사람들끼리 의논해서 간단한 다른 페이지들을 짜 보려고 한다. 

 

 

반응형
반응형

배열 

같은 자료형 데이터들을 하나의 변수에 저장

arrayOf, arrayOfNull이라는 함수를 이용해서 생성 가능

kotlin에서 배열은 Array 클래스로 표현

따라서 set/get 메서드와 size 속성을 기본적으로 포함

 

boxing 오버헤드가 없는 기본 유형 값을 저장하기 위한 전문 배열 클래스를 지원함

기본 타입 Array 형식으로 선언해서 사용함

fun main() {
	var data1 = arrayOf("헤교","지현","수지") // 기본적인 배열 선언
    println("${data1[0]},${data1[1]},${data1[2]}")
}

package practice

fun main() {
    val data2 = arrayOfNulls<Int>(3) // 3개의 정수타입을 저장하는 배열 객체
    data2.set(0,1)
    data2.set(1,2)
    data2.set(2,3)

    val data3 = IntArray(3)  // 위와 동일
    data3.set(0,4)
    data3.set(1,5)
    data3.set(2,6)

    println("${data2[0]}, ${data2[1]} , ${data2[2]}")
    println("${data3.get(0)}, ${data3.get(1)} , ${data3.get(2)}")


}


fun main() {

    val data4 = intArrayOf(10,20,30) // 생성과 동시에 값 초기화
    val data5 = IntArray(3){1} // 3개의 정수를 저장하는 배열에 값 2를 3개의 배열에 넣어준다.
    val data6 = emptyArray<Int>() // 정수타입을 저장하는 빈배열 객체 배열 수가 정해지지 않는다.

            println("${data4[0]},${data4[1]},${data4[2]}")
            println("${data5[0]},${data5[1]},${data5[2]}")
            println(data6.size)

}

 

반응형
반응형

추상 클래스 

abstract라는 키워드로 선언한 클래스

추상 메서드는 abstract라는 키워드를 사용해야 하고

추상 메서드는 반드시 자식 클래스에서 재정의를 해야 함 


abstract class Terran() {
    abstract fun attack()
}

class Marine: Terran() {
    override fun attack() = println("가우스건으로 공격중입니다.")
}

class Firebat: Terran() {
    override fun attack() = println("화염방사기로 공격중입니다.")
}

fun main() {
    var m1 = Marine()
    m1.attack()
    var f1 = Firebat()
    f1.attack()
}


인터페이스 

interface라는 키워드로 추상메서드로 구성된 클래스 정의

추상 메서드는 abstract라는 키워드를 사용해야 하고

추상 메서드는 반드시 자식 클래스에서 재정의를 해야 함

인터페이스 구현 시 : ' :인터페이스명 ' 형태로 사용하고 괄호는 사용하지 않음 


interface Service {
    abstract fun newOne()
    abstract fun read()
    abstract fun readOne()
    abstract fun modify()
    abstract fun delete()
}

object ServiceImpl : Service{
    override fun newOne() = println("객체를 새로 생성합니다")
    override fun read() = println("객체를 불러옵니다.")
    override fun readOne() = println("특정객체를 불러옵니다.")
    override fun modify() = println("특정객체를 수정합니다")
    override fun delete() = println("특정객체를 삭제합니다")
}

fun main() {
// 오브젝트로 선언시 객체생성 단게 필요없이 바로 사용 가능
    ServiceImpl.newOne()
    ServiceImpl.read()
    ServiceImpl.readOne()
    ServiceImpl.modify()
    ServiceImpl.delete()
}

반응형
반응형

Object 

싱글톤 패턴 코드처럼 단일 객체로 선언하고 싶다면

클래스 정의 시 Object를 사용하면 됨

 

싱글톤 패턴?

싱글톤 패턴은 인스턴스가 오직 1개만 생성되어야 하는 경우에 사용되는 패턴이다.

인스턴스가 1개만 생성되는 특징을 가진 싱글톤 패턴을 이용하면,

하나의 인스턴스를 메모리에 등록해서 

여러 스레드가 동시에 해당 인스턴스를 공유하여 사용하게끔 할 수 있으므로

요청이 많은 곳에서 사용하면 효율을 높일 수 있다.  

object Person {
    var name : String =''
    var age : Int = 0
    
    fun print() {
    	print("${name}는 ${age}살이다.) 
    }
}

fun main() {
    Person.name = "가나다"
    Person.age = 52
    Person.print()
}

 


Companion object

자바의 static 변수를 만들고 싶다면

companion object블록에 변수를 정의하고

객체명. 변수명으로 호출하면 됨

class Counter{
	companion object{
    	var cnt = 0
        
        fun add(){
          cnt += 1
        }
    }
}

fun main() {
  var a = Counter.add()
  var b = Counter.add()
  
  println(a)
  println(b)
  println("생성된 Counter 객체 수 :" + Counter.cnt)

}


data class

자바의 VO클래스를 만들고 싶다면

클래스 정의 시 data를 사용하면 됨

클래스 내부에 함수는 포함하면 안 됨

data class Employee(val empno : String, val fnmae: String, val lname : String)

fun main(){
  var emp1 = Employee("1","Lee","Sunwoo")
  var emp2 = Employee("2","Yoon","HyeLynn")
  
  println(emp1.toString())
  println(emp2.toString())
}


코틀린의 클래스 상속

모든 클래스의 조상 클래스는 Any

Any 클래스는 기본적으로 equals, toString, hashCode 등이 멤버 메서드로 구성

부모 클래스를 상속 시  ' : 부모 클래스()' 형태로 작성함

자식 클래스가 상속받으려면 부모 클래스에 open이라고 선언해야 함

부모 클래스의 메서드를 재정의하려면 함수에 open이라고 선언 해야 함

 

open class Parent() {
    val name : String = "Parent"

    fun sayHello() { println("Hello, World!!") }
    open fun sayBye() {println("GoodBye, World!!") }
    }

    class child() : Parent() {
        fun sayAgain() { println("Hello, Kotlin!!") }
        override fun sayBye() { println("GoodBye, Kotlin") }
        }

        fun main() {
            var child = child()

            println(child.name) // 부모클래스의 name변수 가져오기
            child.sayHello()    // 부모클래스의 sayHello() 메서드 가져오기
            child.sayAgain()    // 자식클래스의 sayAgain() 메서드 가져오기
            child.sayBye()      // 부모클래스의 sayBye() 메서드를 자식클래스에서 재정의한 sayBye() 메서드
            // 가져오기
        }

스마트 캐스팅

자손 객체를 부모 타입에 대입하는 경우 암시적 형 변환이 발생 - 이것을 스마트 캐스팅이라 함

반면, 부모 객체를 자손 타입에 대입하는 경우 명시적 형 변환이 필요 -as 키워드 사용용

var p1 : Parent = Child()  // 스마트 캐스팅
var p2 : Child = Parent() // 오류 !! 명시적 형변환이 필요하다.
var p3 : Child = Parent() as Child

 

반응형
반응형

코틀린 함수

함수의 리턴타입이 없는 경우 자바에서는 void였지만

코틀린에는 Unit으로 지정해야 함

ex)

fun 함수명 (매개변수:타입): 리턴타입 { 함수의 몸체 }

 

코틀린은 함수지향 프로그래밍언어이기 때문에

자바와는 달리 클래스의 객체화 없이 바로 호출해서 사용할 수 있음

package practice

fun main() {
    println(add1(1,2))
    add2(3,4)
    println(add3(5,6))
    println(add4(7,8))
}
// 코틀린ver
fun add1(a:Int, b:Int):Int { return a+b}
fun add2(a:Int, b:Int):Unit { println(a+b)} //Unit(void)반환없이 바로 출력문작성

// 람다식ver
fun add3(a:Int, b:Int):Int = a+b // return이랑 {} 생략
fun add4(a:Int, b:Int) = a+b // 반환형까지 생략해 컴파일러에게 추론을 시킬 수 있다.

지명인자 호출을 사용하면

매개변수의 값을 전달할때 매개변수 이름을 같이 지정할 수 있음

또한, 매개변수의 순서도 자유롭게 지정할 수 있음

+

매개변수의 기본값을 지정할 수 있어

인자값을 생략한체로 함수를 호출할 수 있다. 

fun main() {

    println(add(a=3,b=4))
    println(add(b=5,a=6)) //순서변경 가능
    println(add(a=7)) //함수에 기본값을 설정해놔서 b값 생략가능
    println(add()) // 전부생략가능
}
fun add(a:Int =1,b:Int =2)=a+b

반응형
반응형

Kotlin 반복문

for :

지정된 구간에서 조건에 맞춰 반복

for(반복 조건) {반복할 구문}

fun main() {
    for(i in 1..10) print("${i} ") //1~10까지
    println("")

    for(i in 1 until 10) print("${i} ") //1~9까지
    println("")

    for(i in 1..10 step 2) print("${i} ") //1~10까지 홀수 1 2개건너뛰고 3
    println("")

    for(i in 10 downTo 1) print("${i} ") //10~1까지
    println("")

}

while :

조건이 맞는동안 반복

fun main() {
	var i = 10
    while(i>0){
    	print("${i--} ")
    }

}

 

Kotlin 조건식

if :

조건식이 참인 경우 지정된 연산을 수행

fun main() {

	var a = 5
    var b = 10
    
    if(a>b) println("a가 b보다 큽니다")
    else	println("b가 a보다 큽니다")
    println("-----------------------")
    // 조건문 실행결과를 변수의 값에도 저장이 가능하다.
    var max= if(a>b){a}
    		 else{b}
    println("큰값은 ${max}입니다")
    
}

 

when :

정수 이외의 다양한 타입을 대입해서 구분할 수 있음

fun main() {
	var num = 5
    when(num){
    1 -> print("1입니다")
    2 -> print("2입니다")
    in 3..6 -> print("어마어마한 수")
    !is Int -> print("숫자가 아님")
    else -> print("etc")
    }
}

반응형
반응형

Kotlin에서의 변수 종류로는 가변변수와 불변변수가 존재. 

가변변수 : 

변수 선언시 var이라는 키워드를 사용 

변수에 초기값을 선언한 뒤 나중에 값을 변경 가능

 

불변변수 : 

변수 선언 시 val이라는 키워드 사용

변수에 초기값을 선언하면 나중에 값을 변경 못함. 

 

코틀린 자료형

Int, Double, Float, Long, Short, Byte, Char, Boolean, String

Any, Unit, Nothing

 

 

초기값을 이용한 변수 타입 추론

자바와는 다르게 변수의 타입을 지정하지 않아도 됨

변수의 초기값을 이용해서 변수의 자료형을 컴파일러가 추론한다. 

초기값 없이 변수를 선언하는 경우, 컴파일러는 타입을 추론할 수 없음

따라서, 변수의 타입을 선언해야 함

ex) var /val 변수명 : 타입 = 초기값

ex2) var/ val 변수명 : 타입

fun main() {
val name = "이선우" //불변
var kor = 100 // 가변

val eng : Int = 90
var math : Int = 100
}

 

문자열 템플릿

문자열과 변숫값을 합쳐 출력할 때

단, 문자열과 변수를 합쳐서 출력하려면 변수명 앞에 $를 붙임

또한, 변수와 문자열을 붙여서 출력하려면 ${변수명}처럼 사용하면 됨

println("이름은 ${name}입니다.")
println("국/영/수점수는 ${kor},${eng},${math}점 입니다.") 

 

사용자로부터 값 입력받기

readLine() 함수를 사용해서 사용자 입력값을 처리할 수 있음

+ readLine() 함수는 문자형이 기본이어서 정수형 데이터를 입력하려면 

형 변환을 따로 시켜주어야 함.

코틀린에서 형 변환은 to타입명()으로 간단하게 사용할 수 있음

아래 double-bang 예제에 사용 

fun main() {
    print("이름을 입력하세요 :")
    var name = readLine()
    println(name)
}

nullable

코틀린의 타입은 기본적으로 null을 사용할 수 없음 (non-nullable)

따라서, 어떤 변수에 null이 발생할 것 같으면

컴파일러가 귀신같이 알아내 컴파일 오류를 출력함

 

이러한 상황을 방지하려면 null safety operator 중 하나인

double-bang 연산자를 사용하면 됨

이것을 사용함으로써 비록 null이 올 수 있는 변수지만

null이 절대 할당되지 않음을 절대로 단언한다 는 의미를

컴파일러에게 알려줄 수 있음

실제로 NPE이 어디서 발생했는지 알아내는 용도로 사용함

fun main() {
	print("나이를 입력하세요 :")
    var age = readLine!!.toInt()
    print(age)
}

 

또한 안전 호출 연산자(?.)를 이용해서

어떤 변수의 값이 null이면 코드를 실행하고

그렇지 않은 경우 그냥 무시하고 넘어가도록 할 수 있음

 

fun main(){
	var name:String? = null // null을 받아들일수 있도록 변수 선언
    name = name?.capitalize()
    //만일 name이 null이라면 capitalize함수는 실행되지 않음
}	
fun main() {
	
   print("이름을 입력하세요:")
   var name = readLine()?.ifEmpty{"혜교"} //빈값을 입력하면 혜교가 입력되게
   print("나이를 입력하세요:")
   var age = readLine()?.toIntOrNull() ?:0
   // toIntOrNull을 사용하면 Null값이 들어갈수 있게 설정을 해줌 
   // 하지만 ?:0 으로 null값이면 정수 0 이 들어가게 설정함. 
   
   println("이름은 ${name}이고 나이는 ${age}입니다.")
   println("10년 전에는 ${age-10}살이겠네요")
}

 

반응형
반응형

기존 부트스트랩으로 짜 놓은 웹페이지를 직접 Spring mvc를 사용해서 구현을 해보겠다. 

 

첫 번째로 새로운 통합개발환경 툴을 이용해 새로운 프로젝트를 생성한다. 

 

프로젝트를 생성후 WEB-INF 하위 폴더에 

web.xml 설정과 root-context.xml, servlet-context 설정을 해주고 

tiles를 사용하기위해 tiles폴더와 tiles설정을 해준다.

 

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

  <display-name>Archetype Created Web Application</display-name>

  <!-- DispatcherServlet이 자동으로 실행되도록 설정 -->
  <!-- servlet-context.xml 설정정 -->
  <servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!-- root-context.xml 설정 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/root-context.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>


  <!-- filter 설정 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>


</web-app>

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 타일즈 설정 -->
    <bean id="tilesConfigurer"
          class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/tiles.xml</value>
            </list>
        </property>
    </bean>

    <!-- 웹리소스 경로 설정 -->
    <mvc:resources mapping="/**" location="/resources/"/>



    <!-- 타일즈 뷰리졸버 설정 -->
    <bean id="tilesViewResolver"
          class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass"
                  value="org.springframework.web.servlet.view.tiles3.TilesView"/>
        <property name="order" value="1" />
    </bean>

    <!-- 뷰 리졸버 설정 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
        <property name="order" value="1" />
    </bean>

    <!-- 파일업로드 리졸버 설정 : 갤러리 예제에 사용 -->
    <!--    <bean id="multipartResolver"-->
    <!--          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">-->
    <!--        <property name="maxUploadSize" value="10485760" />&lt;!&ndash; 10MB 제한 &ndash;&gt;-->
    <!--    </bean>-->



    <!-- 컨트롤러 자동 스캔-->
    <mvc:annotation-driven/>
    <context:component-scan base-package="imlsw96.spring.mvc"/>

</beans>

root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- jdbc properties -->
    <util:properties id="jdbc"
                     location="/WEB-INF/jdbc.properties"/>
    <!-- jdbc property : 현재 파일에만 속성값 적용 -->
    <context:property-placeholder
            location="/WEB-INF/jdbc.properties" />

    <!-- dbcp datasource -->
    <bean id="dbcpDataSource"
          class="org.apache.commons.dbcp2.BasicDataSource"
          p:driverClassName="${DRV}"
          p:url="${URL}"
          p:username="${USR}"
          p:password="${PWD}" />

    <!-- mybais : sqlSession -->
    <!-- myBatis3 설정 #1 -->
    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean"
          p:dataSource-ref="dbcpDataSource"
          p:mapperLocations="classpath*:mybatis3/*Mapper.xml" />

    <!-- myBatis3 설정 #2 -->
    <bean id="sqlSession"
          class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory" index="0" />
    </bean>


</beans>

 

jdbc.properties 보안 때문에 안 올림

 

 

tiles폴더 안에는 매 페이지마다 중복이 되는 header/footer/modal/ 부분의 jsp파일로 구성해서 

template파일에 넣어주고 페이지마다 달리 표현되어야 할 메인 부분을 상황에 맞게 채워주기 위해 

jsp폴더 아래에 넣어준다. 이번 글에서는 index페이지 즉 홈 화면을 구성하기 위해 index.jsp를 넣는다. 

그리고 resources파일 아래에 웹 리소스 파일들을 넣어 css/js/img 등 웹에 필요한 자원들을 넣어준다. 

부트스트랩으로 작성했기에 부트스트랩 전용 css 파일들이 필요하다

 

넣어준 후 IndexController 클래스를 만들어 홈 화면을 띄우는 작업을 해보자. 

 

IndexController

package imlsw96.spring.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {

    @GetMapping("/index")
    public String index() {
        return "index.tiles";
    }
}

 

어노테이션으로 컨트롤러와 GetMapping작업을 해준다. 

톰캣 웹서버를 열면 localhost:8080의 기본 url이 지정되는데 여기서 

localhost:8080/index url을 요청 시 return index.tiles 즉 idnex.jsp파일을 가져와 

template.jsp 파일의 main부분에 넣은 화면을 전달한다. 

 

이과정을 나는 이렇게 이해했다 

servlet-context에서 타일즈를 뷰 리졸버와 jsp파일이 담겨있는 경로로 뷰 리졸버를 설정해주었고 

tiles.xml에서는 base의 이름을 가진 template.jsp에 main부분을 ooo.tiles라는 이름으로 설정하면

ooo.jsp파일을 찾아 main에 넣어주는 걸 확인할 수 있는데

결과적으로 template 기본골격을 가진 jsp파일에 index의 메인 부분을 더해 완벽한 index페이지가 나오는 것 같다.

 

반응형
반응형

MVC에서 타일즈를 사용해서 만들면 

웹페이지에서 반복적으로 출력이 되는 부분 header 라던지 footer의 내용이 중복되는 부분을 

뽑아놔서 모든 페이지에 적용하고 페이지마다 출력이 다르게 될 메인 부분만을 수정해서 해서 

유지보수에 도움을 많이 준다. 

 

타이즈를 사용하기 위해서 servlet-context.xml라는 이름을 가진 서블릿 파일에다 

타일즈 설정 /타일즈 뷰 리졸버 설정/ 등 부가적으로 필요한 웹 리소스/컨트롤러 스캔이 필요한 설정 코드들을 작성해준다.

 

+ 이 작업 이전에 maven pom.xml에 라이브러리 르 추가해주어야 한다.

pom.xml

	<!-- Tiles -->
	<dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-extras</artifactId>
            <version>3.0.8</version>
          </dependency>
        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-servlet</artifactId>
            <version>3.0.8</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-jsp</artifactId>
            <version>3.0.8</version>
        </dependency>

 

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 타일즈 설정 -->
<bean id="tilesConfigurer"
      class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
    <list>
        <value>/WEB-INF/tiles.xml</value>
    </list>
</property>
</bean>

    <!-- 웹리소스 경로 설정 -->
    <mvc:resources mapping="/**" location="/resources/"/>



    <!-- 타일즈 뷰리졸버 설정 -->
<bean id="tilesViewResolver"
      class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
          value="org.springframework.web.servlet.view.tiles3.TilesView"/>
<property name="order" value="1" />
</bean>

    <!-- 컨트롤러 자동 스캔-->
    <mvc:annotation-driven/>
    <context:component-scan base-package="controller"/>

</beans>

 

이렇게 설정을 끝내고 나면 이제 

반복적으로 출력해야 할 부분의 파일들을 설정해주는 tiles.xml을 생성해서 설정해주어야 한다. 

 

 

tiles.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- 모든 xml 파일에 맨첫줄에 있어야할 태그 -->
<!DOCTYPE tiles-definitions PUBLIC
        "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
        "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">


<tiles-definitions>
    <!-- 레이아웃 정의 -->
    <!-- 변하지 않는 부분들로 구성된 페이지를 템플릿으로 선언 -->
    <definition name="base" template="/WEB-INF/tiles/template.jsp">
            <put-attribute name="header" value="/WEB-INF/tiles/header.jsp"/>
            <put-attribute name="footer" value="/WEB-INF/tiles/footer.jsp"/>
            <put-attribute name="modal" value="/WEB-INF/tiles/modal.jsp"/>
    </definition>

    <!--메인영역에 보여줄 페이지를 url 요청방법에 따라 구분해 둠-->
    <!-- 메인영역을 제외한 나머지 영역은 위에서 정의한 템플릿을 참조함 -->
    <!-- url:/index.tiles => main: /index.jsp -->
    <!-- 별의 갯수에 따라 인자값이 변경가능 -->
    <definition extends="base" name="*.tiles">
        <put-attribute name="main" value="/WEB-INF/jsp/{1}.jsp"/>
    </definition>

    <!-- url : /join/agree.tiles => main : / join/agree.jsp -->
    <!-- url: /board/list.tiles => main :/board/list.jsp -->
    <definition extends="base" name="*/*.tiles">
        <put-attribute name="main" value="/WEB-INF/jsp/{1}/{2}.jsp"/>
    </definition>
</tiles-definitions>

위의 코드들을 보면 일단 

맨 위의 파란색 코드들은 타일즈 코드임을 나타내 주는 코드와 UTF-8 설정해주는 코드이고 

 

tiles-definitions태그를 이용해서 중복으로 사용될 header/footer/modal 부분을 설정해주고 

파일 위치를 지정해주고 나서 template.jsp파일에 사용이 가능하게 설정해준다. 

 

그리고 아래에 페이지마다 다르게 출력이 되는 메인 부분을 저장하는 경로를 설정해주고 url요청에 따라 

요청에 맞는 페이지를 출력해주기 위해 경로를 value에 값에 설정한다. 

 


 

완성된 template.jsp

<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ page contentType="text/html;charset=UTF-8" %>

<!doctype html>
<html lang="ko">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
    <link rel="stylesheet" href="/css/semiproject.css">

    <title>세미프로젝트v1</title>
    <style>
        .fatdiv { padding: 15px; }
        .margin30 { margin: 30px 0; }
    </style>
</head>
<body>
<div class="container">
   <tiles:insertAttribute name="header"/>

    <tiles:insertAttribute name="main"/>

    <tiles:insertAttribute name="footer"/>
</div>

<!-- 로그인 모달 -->
<tiles:insertAttribute name="modal"/>

<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="/js/bootstrap.bundle.min.js"></script>
</body>
</html>

 

탬플릿이라는 기본 페이지가 완성이 되었으면 우리는 이제 메인 부분을 신경 써야 하는데

메인 부분에서는 요청 url마다 다른 파일들로 이루어져 있는데 그 파일에도 header/modal/footer

부분이 중복적으로 남아있는데 중복되는 부분은 template.jsp가 해결해주니 main부분만 남기고 다 날려 버리면 된다 

 

예를 들어 index.jsp를 확인해 보자

<%@ page pageEncoding="utf-8" %>
<div id="main margin30">
    <div class="row text-center">
        <div class="col">
            <h1 class="display-3 margin30">超機密 PROJECT 補完計劃</h1>
            <img src="/resources/img/BrooklynNets/이미지%202021-01-13-41.png" class="margin30">
            <p class="margin30">Maecenas luctus dignissim magna, vitae iaculis lorem ultricies eu. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas scelerisque lectus porttitor tellus scelerisque, vel placerat ipsum pulvinar. Donec ut convallis sem. Curabitur hendrerit nulla vitae turpis viverra, at sodales lacus aliquet. Aenean id posuere neque, quis pulvinar sapien. Aliquam vestibulum maximus nibh at dapibus. Quisque feugiat egestas elementum. Nunc vulputate imperdiet augue, sed rutrum ipsum fermentum ac. Sed porttitor rhoncus tempus. Aliquam dapibus consequat orci, eu fringilla erat facilisis vel. Phasellus sagittis nulla ac sem efficitur convallis. Mauris convallis metus quis efficitur interdum. Etiam eget nunc lectus. Duis feugiat ipsum sit amet justo dignissim, sed hendrerit lectus sodales. Maecenas volutpat, nunc ut viverra lobortis, libero ipsum laoreet mauris, quis efficitur ante nisi sed ex.</p>
            <div><button type="button" class="btn btn-success">지금 바로 시작하기</button></div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-4 margin30">
            <h2>極秘</h2>
            <p>Fusce quam sem, ornare vel pulvinar id, suscipit ac leo. Mauris vel nibh in est efficitur porttitor. Etiam quis malesuada tellus. Aliquam ac mattis turpis. Proin suscipit et lorem sed ultricies. Sed augue nibh, tincidunt eget accumsan in, mollis id risus. Sed interdum ipsum sed diam convallis mattis. Fusce euismod viverra nisi, a consectetur erat vehicula nec. Pellentesque ac mi ligula. Vivamus molestie mauris quis nisl fermentum, ut luctus enim hendrerit. Duis sagittis et sapien quis posuere. Morbi ac lacinia tortor, eu sagittis quam. Donec aliquet augue et mauris elementum consectetur. Maecenas nisi tortor, euismod accumsan elit a, ornare tincidunt ligula. Vestibulum luctus lorem nec rhoncus placerat.</p>
            <div><button type="button" class="btn btn-light">자세히 보기 &blacktriangleright;</button></div>
        </div>
        <div class="col-md-4 margin30">
            <h2>誤謬</h2>
            <p>Fusce quam sem, ornare vel pulvinar id, suscipit ac leo. Mauris vel nibh in est efficitur porttitor. Etiam quis malesuada tellus. Aliquam ac mattis turpis. Proin suscipit et lorem sed ultricies. Sed augue nibh, tincidunt eget accumsan in, mollis id risus. Sed interdum ipsum sed diam convallis mattis. Fusce euismod viverra nisi, a consectetur erat vehicula nec. Pellentesque ac mi ligula. Vivamus molestie mauris quis nisl fermentum, ut luctus enim hendrerit. Duis sagittis et sapien quis posuere. Morbi ac lacinia tortor, eu sagittis quam. Donec aliquet augue et mauris elementum consectetur. Maecenas nisi tortor, euismod accumsan elit a, ornare tincidunt ligula. Vestibulum luctus lorem nec rhoncus placerat.</p>
            <div><button type="button" class="btn btn-light">자세히 보기 &blacktriangleright;</button></div>
        </div>
        <div class="col-md-4 margin30">
            <h2>警告</h2>
            <p>Fusce quam sem, ornare vel pulvinar id, suscipit ac leo. Mauris vel nibh in est efficitur porttitor. Etiam quis malesuada tellus. Aliquam ac mattis turpis. Proin suscipit et lorem sed ultricies. Sed augue nibh, tincidunt eget accumsan in, mollis id risus. Sed interdum ipsum sed diam convallis mattis. Fusce euismod viverra nisi, a consectetur erat vehicula nec. Pellentesque ac mi ligula. Vivamus molestie mauris quis nisl fermentum, ut luctus enim hendrerit. Duis sagittis et sapien quis posuere. Morbi ac lacinia tortor, eu sagittis quam. Donec aliquet augue et mauris elementum consectetur. Maecenas nisi tortor, euismod accumsan elit a, ornare tincidunt ligula. Vestibulum luctus lorem nec rhoncus placerat.</p>
            <div><button type="button" class="btn btn-light">자세히 보기 &blacktriangleright;</button></div>
        </div>
        <div class="col-md-4 margin30">
            <h2>危險</h2>
            <p>Fusce quam sem, ornare vel pulvinar id, suscipit ac leo. Mauris vel nibh in est efficitur porttitor. Etiam quis malesuada tellus. Aliquam ac mattis turpis. Proin suscipit et lorem sed ultricies. Sed augue nibh, tincidunt eget accumsan in, mollis id risus. Sed interdum ipsum sed diam convallis mattis. Fusce euismod viverra nisi, a consectetur erat vehicula nec. Pellentesque ac mi ligula. Vivamus molestie mauris quis nisl fermentum, ut luctus enim hendrerit. Duis sagittis et sapien quis posuere. Morbi ac lacinia tortor, eu sagittis quam. Donec aliquet augue et mauris elementum consectetur. Maecenas nisi tortor, euismod accumsan elit a, ornare tincidunt ligula. Vestibulum luctus lorem nec rhoncus placerat.</p>
            <div><button type="button" class="btn btn-light">자세히 보기 &blacktriangleright;</button></div>
        </div>
        <div class="col-md-4 margin30">
            <h2>隔離</h2>
            <p>Fusce quam sem, ornare vel pulvinar id, suscipit ac leo. Mauris vel nibh in est efficitur porttitor. Etiam quis malesuada tellus. Aliquam ac mattis turpis. Proin suscipit et lorem sed ultricies. Sed augue nibh, tincidunt eget accumsan in, mollis id risus. Sed interdum ipsum sed diam convallis mattis. Fusce euismod viverra nisi, a consectetur erat vehicula nec. Pellentesque ac mi ligula. Vivamus molestie mauris quis nisl fermentum, ut luctus enim hendrerit. Duis sagittis et sapien quis posuere. Morbi ac lacinia tortor, eu sagittis quam. Donec aliquet augue et mauris elementum consectetur. Maecenas nisi tortor, euismod accumsan elit a, ornare tincidunt ligula. Vestibulum luctus lorem nec rhoncus placerat.</p>
            <div><button type="button" class="btn btn-light">자세히 보기 &blacktriangleright;</button></div>
        </div>
        <div class="col-md-4 margin30">
            <h2>制限</h2>
            <p>Fusce quam sem, ornare vel pulvinar id, suscipit ac leo. Mauris vel nibh in est efficitur porttitor. Etiam quis malesuada tellus. Aliquam ac mattis turpis. Proin suscipit et lorem sed ultricies. Sed augue nibh, tincidunt eget accumsan in, mollis id risus. Sed interdum ipsum sed diam convallis mattis. Fusce euismod viverra nisi, a consectetur erat vehicula nec. Pellentesque ac mi ligula. Vivamus molestie mauris quis nisl fermentum, ut luctus enim hendrerit. Duis sagittis et sapien quis posuere. Morbi ac lacinia tortor, eu sagittis quam. Donec aliquet augue et mauris elementum consectetur. Maecenas nisi tortor, euismod accumsan elit a, ornare tincidunt ligula. Vestibulum luctus lorem nec rhoncus placerat.</p>
            <div><button type="button" class="btn btn-light">자세히 보기 &blacktriangleright;</button></div>
        </div>
    </div>
</div>

 

 

 이게 현재 지금 내가 학원에서 작업하고 있는 웹페이지의 맨 처음 메인화면인 index.jsp 파일인데 

보다시피 메인 부분만을 남겨놓고 나머지는 다 날려놨다

 

이제 기본적인 세팅은 끝나고 이 웹페이지를 맞는 url을 만들어 요청이 되면 거기에 해당하는 페이지가 나오게 하기 위해 

컨트롤러 부분을 보면 된다.


IndexController

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class Indexcontroller {

    @GetMapping("/index")
    public String index(){
        // 타일즈 없는 뷰 호출
        // return "index";
        // => /WEB-INF/jsp/ + index + . jsp

        // 타일즐 템플릿 기반 뷰 호출
        // template.jsp <-/WEB-INF/jsp/index.jsp
        return "index.tiles";
    }
}

 이전 Spring4 MVC 실습 1에서 했듯이 어노테이션 GetMapping을 해주어서 return값에 index.tiles로 지정해주면

알아서 index.jsp 파일 경로를 찾아서 메인 부분에 template부분에 추가해줘서 띄워지게 된다. 

 

코드를 보고 든 의문점 1

어떻게 index.tiles를 하면 찾는가?

이전에 tiles.xml에서 설정해주었다 아래 코드를 다시 봐보자.

    <!--메인영역에 보여줄 페이지를 url 요청방법에 따라 구분해 둠-->
    <!-- 메인영역을 제외한 나머지 영역은 위에서 정의한 템플릿을 참조함 -->
    <!-- url:/index.tiles => main: /index.jsp -->
    <!-- 별의 갯수에 따라 인자값이 변경가능 -->
    <definition extends="base" name="*.tiles">
        <put-attribute name="main" value="/WEB-INF/jsp/{1}.jsp"/>
    </definition>

 

위의 코드는 tiles.xml의 일부분이고 코드를 봐보면 우리는  블라블라. tiles로 지정을 해두면 

value에 설정해준 경로로 찾아가 template의 main에 넣어준다는 걸 확인할 수 있다.

 

 

여기서 생기는 의문점 2

찾아서 template.jsp에 main부분에 필요한 부분을 채워준다는 건 확인할 수 있는데 

index페이지에 템플릿이 왜 출력이 되는가?

 

이 부분은 우리가 처음 우리가 설정해준 servlet-context파일을 다시 볼 필요가 있다. 

코드를 보면 우리는 tiles.xml을 가져오고 타일즈를 뷰 리졸버로 설정해준 걸 확인할 수 있다. 

그렇게 tiles.xml에는 template를 기본 base 페이지 골격을 설정해주고 main부분을 

java 클래스코 드에서 index.tiles 등 main부분을 꾸며줄 jsp 파일들의 이름으로. tiles를 붙이면

우리가 설정해준 대로 알아서 뷰 객체를 찾아서 적용시켜준다고 나는 이렇게 뇌피셜을 굴려 이해했다. 

 

이렇게 나온 뇌피셜이 내가 수업시간에 따라한 코드를 보고 도출해낼 수 있는 합리적인 뇌피셜이라고 생각한다. 

물론 구글링을 통해도 찾아보았지만 버전이 달라서 그런지 몰라도 페이지를 구현하고 xml파일의 코드 구성이 제각각이라 이해하는 데 더 어려움을 받았다.  그래도 뭔가 혼자 코드를 보며 계속 파일들을 넘나들며 도출해낸 생각들이 내 딴에는 꽤나 합리적이고 이해하는데 있어서 말의 아귀가 안 맞는다는 느낌은 들지 않아서 꽤나 만족스럽다.. 

 

틀린 부분이나 잘못 생각한 부분이 있다면 알려주세요 볼 사람이 있을지는 모르겠지만 ㅋㅋㅋㅋㅋ

반응형
반응형

만드는 순서 등을 복습 겸 정리. 

뇌피셜의 향연

 

pom.xml로 필요라이브러리를 다운로드하게 설정해준다.

 

https://www.youtube.com/watch?v=9Tmzt6Q9WI8

 

그다음 web.xml설정을 해준다.

web.xml은 웹 어플리케이션의 환경설정 파일이라고 보면 된다. 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">


  <display-name>HelloSp4MVC</display-name>

  <!-- DispatcherServlet이 자동으로 실행되도록 설정 -->
  <!-- servlet-context.xml 설정정 -->
  <servlet>
    <servlet-name>HelloSp4MVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloSp4MVC</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!-- root-context.xml 설정 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/root-context.xml</param-value>
  </context-param>


  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- filter 설정 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

 

 

위의 코드를 보면 

dispatcher역할을 해줄 servlet-context 파일과 

JDBC의 정보를 담은 root-context파일을 설정해준걸 확인할 수 있다.

 

servlet-context파일에서는 뷰에 저장되있는 클라이언트에게 띄워줄 화면이 담아져 있는 위치 정보(jsp)를 포함하고 추가적으로웹 리소스 경로/ 파일 업로드 리졸버/ 컨트롤러 클래스 지정해주는 내용이 담아져 있다.

 

servlet-context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--컨트롤러 클래스에 어노테이션을 사용하겠다는 의미 -->
    <mvc:annotation-driven/>
    <!-- 뷰 리졸버 설정 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
        <property name="order" value="1" />
    </bean>

    <!-- 웹 리소스 경로 지정 -->
    <mvc:resources mapping="/resources/**"
                   location="/resources/" />
    <!-- 파일업로드 리졸버 설정 -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10485760" /><!-- 10MB 제한 -->
    </bean>
    <!-- 컨트롤러 클래스 지정 -->
    <context:component-scan base-package="imlsw96.spring.mvc"/>

</beans>

root-context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">


    <!-- jdbc properties -->
    <util:properties id="jdbc"
                     location="/WEB-INF/jdbc.properties"/>


    <!-- dbcp datasource -->
    <bean id="dbcpDataSource"
          class="org.apache.commons.dbcp2.BasicDataSource"
          p:driverClassName="${DRV}" p:url="${URL}"
          p:username="${USR}" p:password="${PWD}" />
</beans>

IndexController

package imlsw96.spring.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

import java.text.DateFormat;
import java.util.Date;

@Controller
public class IndexController {

    @GetMapping("/first")
    public String first() {
        return "first";
    }

    @GetMapping("/today")
    public ModelAndView today(){
        ModelAndView mv = new ModelAndView();
        mv.setViewName("today");
        mv.addObject("today",getToday());
        return mv;
    }

    private String getToday() {
        Date date = new Date();
        // 날짜와 시간을 자세히 출력하는 형식 객체 정의
        DateFormat dateFormat=
                    DateFormat.getDateTimeInstance(
                            DateFormat.LONG, DateFormat.LONG );
        // date 객체를 DateFormat 객체를 통해
        // 자세한 날짜와 시간으로 변환
        String formattedDate = dateFormat.format(date);
        return formattedDate;
    }
}

 

어노테이션 @GetMapping의 처음 부분을 확인해보자. 

GetMapping("/first") 부분은 url을 지정해주는 것 같고 

아래 리턴으로 first.jsp파일을 리턴해주는 것 같다. first.jsp파일을 반환한다고 생각한 이유로는 

servlet-context에 뷰 리졸버로 jsp파일 경로를 지정해주었기 때문이다. 

추가적으로 컨트롤러 클래스 위치까지 지정해주어서 킹리적갓심이라고 해야 할까 

 

그렇게 하고 톰캣 서버를 열어서 확인해보면 잘 나온다.

first.jsp

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Hello,World !!</title>
</head>
<body>
<h1>매우매우 졸리다 </h1>
<p>시간은 금이라구 친구 !!  내말 믿으라구 이말이야 ~</p>
</body>
</html>

위의 <% page contentType%> 이 부분은 html 문법을 사용할 수 있게 하는 태그 같다. 

 

그리고 두 번째 @GetMapping 어노테이션인 /today 부분을 보면

ModelAndView객체를 리턴하는 걸 확인할 수 있는데 ㅋㅋㅋ 뇌피셜로 지껄여보자면 

전에 올린 MVC글에서 보면

모델 애플리케이션의 정보(데이터)를 나타내며

 텍스트, 체크박스 항목 등과 같은 사용자 인터페이스 요소를 나타내고, 라는 부분을

확인할 수 있는데 and 두 개로 묶은 걸 보니까 데이터와 뷰 페이지 둘 다 혼용하는 뭐 그런 객체가 아니겠는가 

그렇게 코드르 분석해보면 setViewName 메서드로 today.jsp를 view페이지로 설정해주고 

addObject로 객체를 넣어주는데 이름은 today요 추가할 부분은 getToday()라는 메서드이다 

 

getToday메서드는 뭔가 하고 봤더니 아래 코드에 

현재 시간을 생성하는 메서드가 아닌가 

간단하게 실험하기 위해 클래스 내부에 생성해서 사용했다  

이제 today.jsp 파일을 봐보자.

 

<%@ page contentType="text/html;charset=UTF-8"  %>
<%@ taglib uri ="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
    <title>오늘 날짜, 시간 출력</title>
</head>
<body>
<h1>날짜, 시간 출력</h1>
<p>${today}</p>
</body>
</html>

 

 

봐보면 <p> 태그에 ${today}를 확인할 수 있고 맨 위에 태그들 중 못 보던 taglib 태그를 볼 수 있는데 

아까 컨트롤러에서 addObject로 객체를 추가한 걸 기억할 수 있는데 아마 html문에서 컨트롤러의 태그를 사용하기 위해서는

taglib 가 필요하고 추가한 객체를 사용하기 위해서는 ${} 달러표시를 통해 하는 거 같다. 

 

많은 시간 동안 수업내용에 대해 복습하는 글을 많이 못 올렸는데

못 올린 이유에는 일단 수업 자체가 확 어려워졌다고 느껴졌다. 

수업이 끝나는 기간은 정해져 있는데 해나가야 할 부분은 많다 보니 

수업내용에서도 많은 부분이 자세한 설명 없이 넘어가고 수업 대부분의 시간을 선생님의 코딩을

그저 따라갔다. 이해보다는 이렇게 하니 이런 결과물이 출력이 된다 정도로만 수업이 진행되었다고 

개인적으로 생각되는데 결과적으로 이런 수업을 듣고 나서 저녁시간 때 복습을 하려고 

수업의 결과물을 보니 행위의 결과물들만 남아있다고 느껴졌다. 

 

그렇게 복습을 하려고 하니 행위에 대한 이유? 저건 뭐고 이건 뭐지 하는 부분을 온전히 내가 공부해야 하는

상황이었는데  모르는 부분을 구글링을 하다 보니 모르는 걸 검색했더니 모르는 게 더 튀어나오는 상황을 겪었다.

 

이럴 때 내 안 좋은 습관들이 다시 나온 거 같다. 

어찌어찌 모르는 걸 해결하려고는 하는데 계속 모르는 늪에 깊숙이 계속 빠지는듯한 기분에 그냥 빠져나와서 

영화도 보고 운동도 하고 유튜브도 보고 머릿속에는 계속해야지 해야지 했는데 모르는 것들만 튀어나오니 손에 잡아야 한다는 걸 알지만 잡히진 않았다. 그렇게 수업을 듣고 의무적으로 들어야 하는 온라인 수업을 틀어놓으며 딴짓들을 요 며칠 했는데 당연한 이야기겠지만 위의 방법으로는 해결이 불가능하고 끝에는 포기를 하게 되는데

이 경험을 나는 이미 해봤고 포기는 하고 싶지 않아서 다시 늪에 들어가 발버둥 치려고 들어와 봤다. 

 

복습을 하면서 음 구글링으로 이게 뭔지 정확히 아는 것도 중요한 부분이라고 생각한다. 하지만 그 정확함을 알기 위해 

했던 행동들이 더욱 힘들게 했다. 그래서 일단 뇌피셜로 추측해가며 일단 복습해 나가고 그렇게 하는 와중에 오류가 생기면 다시 또 찾아보고 그렇게 나의 뇌피셜을 수정해나가는 방식으로 다시 시작해보려고 한다. 

 

 

 

 

반응형
반응형

MVC 패턴

MVC (model - view - controller)

사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴이다. 

소프트웨어의 비즈니스 로직과 화면을 구분하는데 중점을 두고 있다. 

 

애플리케이션 개발 시 사용자가 인터페이스로부터 비즈니스 로직을 분리하여

애플리케이션의 시각적 요소나 그 이면에서 실행되는 비즈니스 로직을 서로 영향 없이 쉽게 고칠 수 있다. 

 

MVC에서

모델애플리케이션의 정보(데이터)를 나타내며

텍스트, 체크박스 항목 등과 같은 사용자 인터페이스 요소를 나타내고,

컨트롤러데이터와 비즈니스 로직 사이의 상호동작을 관리한다

이러한 "관심사 분리"는 더 나은 업무의 분리와 향상된 관리를 제공한다. 

 

MVC에 기반을 둔 몇 가지 다른 디자인 패턴으로는 

MVVM (모델-뷰-뷰모델), MVP(모델-뷰-프리젠터) MVW(모델-뷰-왓에버)가 있다.

 

디자인 패턴이란? 

- 객체 지행 프로그래밍 설계를 할 때 자주 발생하는 문제들을 피하기 위해 사용되는 형식을 의미.

 

 

MVC 패턴의 중요 구성요소

Controller (컨트롤러) :

클라이언트의 요청을 받았을 때, 그 요청에 대해 실제 업무를 수행하는 모델 컴포넌트를 호출

또한 클라이언트가 보낸 데이터가 있다면, 모델에 전달하기 쉽게 데이터를 가공한다. 

모델이 업무를 마치면 그 결과를 뷰에게 전달한다. 

 

Model (모델) :

컨트롤러가 호출할 때, 요청에 맞는 역할을 수행한다. 

비즈니스 로직을 구현하는 영역으로 응용프로그램에서 데이터를 처리하는 부분.

 

비즈니스 로직? 

- 업무에 필요한 데이터처리를 수행하는 응용프로그램의 일부라고 할 수 있다. 

  DB에 연결하고 데이터를 추출하거나 저장, 삭제, 업데이트, 변환 등의 작업을 수행.

  상태의 변화가 있을 때 컨트롤러와 뷰에 통보해 후속 조치 명령을 받을 수 있게 한다.

 

View(뷰):

컨트롤러부터 받은 모델의 결과값을 가지고 사용자에게 출력할 화면을 만드는 일을 한다. 

만들어진 화면을 웹브라우저에 전송하여 웹브라우저가 출력하게 하는 것.

화면에 표시되는 부분으로 추출한 데이터나 일반적인 텍스트 데이터를 표시하거나 

입력 폼 또는 사용자와의 상호작용을 위한 인터페이스를 표시하는 영역이다.

 

Spring MVC

Spring 자체적으로 제공하는 MVC 프레임워크를 사용하면,

Spring이 제공하는 AOP, 트랜잭션 처리, DI 등의 기능을 그대로 사용하면서

MVC 패턴에 기반하여 웹 어플리케이션을 개발할 수 있다.

또한 스트러츠와 Spring을 연동하기 위해 설정의 중복과 같은 개발 과정상의 불편을 해소할 수 있다.

컨트롤러 중에서도, 맨 앞단에서 유저의 요청을 받는 컨트롤러를 프론트 컨트롤러라 한다. 

  • DispatcherServlet 객체가 이 역할을 한다.

  • 본격적으로 로직에 들어오기 전에, 요청에 대한 선처리 작업을 수행한다.


DispatcherServlet?

  • SpringFramework가 제공하는 Servlet클래스 
  • 사용자의 요청을 받는다.
  • Dispatcher가 받은 요청은 HandlerMapping으로 넘어간다.

 

 

프런트 컨트롤러는 요청을 핸들러 매핑을 통해 해당 요청을 어떤 핸들러가 처리해야 하는지를 매핑한다.

  • HandlerMapping 객체가 핸들러 매핑에 대한 정보를 담고 있다. 


HandlerMapping?

  • 사용자의 요청을 처리할 Controller를 찾는다. (Controller URL Mapping)

  • 요청 url에 해당하는 Controller 정보를 저장하는 table을 가진다.

  • 즉, 클래스에 @RequestMapping("/url") annotaion을 명시하면 해당 URL예 또한 요청이 들어왔을 때 table에 저장된 정보에 따라 해당 클래스 또는 메서드에 Mapping 한다.

 

 

이렇게 매핑된 핸들러를 실제로 실행하는 역할은 핸들러 어댑터가 담당한다.

  • HandlerAdapter 객체가 이 역할을 한다. 

컨트롤러는 해당 요청을 처리하는 로직을 담고 있다.

  • 보통 요청의 종류 혹은 로직의 분류에 따라 내부적으로 Service 단위로 나누어 모듈화 한다.

  • 각 서비스에서는 DB 접근할 수 있는 Repository 객체(DAO)를 이용하여 데이터에 접근할 수 있다. 

컨트롤러는 서비스에서의 로직 처리 후, 결과를 뷰 리졸버를 거쳐 뷰 파일을 렌더링 하여 내보낸다.

  • ViewResolver 객체가 이 역할을 한다.

ViewResolver?

  • Controller가 반환된 View Name (the logical names)에 prefix, suffix를 적용하여 ViewObject(the physical view files)를 반환한다.

  • 예를 들어 view name:home, prefix: /WEB-INF/views/,  suffix: jsp는 /WEB-INF/views/home.jsp라는 위치의 View(JSP) 예 Controller에게 받은 Model을 전달한다.

  • 이후에 해당 View에서 이 Model data를 이용하여 적절한 페이지를 만들어 사용자에게 보여준다.

반응형
반응형

스프링 프레임워크를 사용하지 않은 기존 JDBC API 이용 시 문제점

1. Connection/ PreparedStatement/ ResultSet 객체 관리의 복잡성

2. 복잡한 예외/ SQL Exception 처리문제 - 원인 파악의 모호

3. DriverMangaer/ JNDI를 이용한 Connection 객체 취득 문제 

 

스프링 프레임워크의 JDBC기능을 이용하면 위의 문제들이 대부분 해결

 

Persistence API ? 

RDBMS와 OOP 객체 사이의 불일치에서 오는 패러다임을 해결하기 위해 

자바는 ORM(Object-Relational Mapping) 기술을 만들었다. 

문자 그대로 ORM은 객체와 RDBMS를 매핑하는 역할을 한다. 

  • JDBC : 자바 표준 API
  • Hibernate : ORM 기반 persistence API
  • JPA : J2EE 표준 API, hibernate를 기반으로 제작
  • myBatis : SQL Mapping 기반 persistence API
  • JDBC와 myBatis를 사용

 

Chapter 01 : 표준 JDBC API -소규모 ver

필요 라이브러리(의존성 추가)

   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.30.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.3.30.RELEASE</version>
    </dependency>
    
        <dependency>
      <groupId>org.mariadb.jdbc</groupId>
      <artifactId>mariadb-java-client</artifactId>
      <version>2.7.1</version>
    </dependency>
    
        <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-dbcp2</artifactId>
      <version>2.8.0</version>
    </dependency>

JDBC, DBCP?

자바 웹 애플리케이션에서 DB 접속과 관련된 라이브러리가 JDBC, DBCP이다. 

JDBC는 JavaDataBase Connectivity의 약자로 자바에서 데이터베이스에 연결하기 위한 인터페이스.

Oracle, MySQL 등 데이터 베이스는 JDBC를 사용하기 위한 각각의 Driver를 제공

JDBC는 이 Driver를 통해 DB에 접속 하지만 JDBC만으로는 효율적인 연결이 불가능하다. 

이럴 때 사용하는 게 DBCP 

DBCP는 DataBase Connection Pool의 약자로 DB와 커넥션을 맺고 있는 객체를 관리하는 역할을 한다. 

 

JDBC만을 사용할 때 DB 접속 순서

1. DB 접속을 위한 JDBC 드라이버 로드

2. getConnection Method로부터 DB 커넥션 객체를 얻음

3. 쿼리 수행(SQL)을 위한 PreparedStatement 객체 생성

4. excuteQuery를 실행해서 결과를 받아옴. 

 

여기서의 비효율적인 부분은 1번과 2번

DB 연결 시마다 Driver를 로드하고 커넥션 객체를 얻는 작업을 반복.

이 부분을 효율적으로 처리하도록 바꾸는 것이 DBCP의 역할

 

DBCP를 사용하게 되면

WAS(웹 애플리케이션 서버) 실행 시 미리 일정량의 DB Connection 객체를 생성하고 Pool이라는 공간에 저장

그리고 DB 연결 요청이 있으면 이 Pool 이라는 공간에 Connection 객체를 가져다 쓰고 반환하게 된다. 

 

#spring4 data01. xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 어노테이션 기반 bean 설정-->
    <context:component-scan base-package="imlsw96.data.service, imlsw96.data.dao"/>

    <!-- 표준 JDBC API : 소규모 -->
    <bean id="basicDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.mariadb.jdbc.Driver"/>
        <property name="url" value="jdbc:mariadb://mariadb.cw2h1nljbpsk.ap-northeast-2.rds.amazonaws.com:3306/playground"/>
        <property name="username" value=""/> <!-- 보안의 이유로 value 값 x -->
        <property name="password" value=""/>
    </bean>


    <!-- 스프링 JDBC template 정의 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="basicDataSource"/>
    </bean>
</beans>

스프링 JDBC template?

Spring Framework는 JDBC 프로그래밍을 위해 JdbcTemplate 클래스를 제공하며 

JdbcTemplate 클래스는 손쉽게 DB와 연동할 수 있도록 구현되어 있다.

JdbcTemplate의 메서드를 사용해서 insert, select, update, delete를 구현할 수 있다 (DAO 클래스에 쓰일 예정)

 

#HelloSpring4DataApp01 (Main)

 

package imlsw96.data;

import imlsw96.data.service.MemberService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jca.context.ResourceAdapterApplicationContext;

public class HelloSpring4DataApp01 {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring4data01.xml");
        MemberService msrv = (MemberService) ctx.getBean("msrv");

        // 회원정보 생성
        System.out.println(msrv.newMember());


        // 회원정보 조회 ( 아이디, 등급, 가입일)
       // System.out.println(msrv.readMember());

        // 회원정보 상세조회(아이디로 검색, 모든컬럼출력)
        //System.out.println(msrv.readOneMember());

        //회원정보 수정
        //System.out.println(msrv.modifyMember());

        //회원정보 삭제
        System.out.println(msrv.removeMember());

    }
}

#MemberService

package imlsw96.data.service;


import imlsw96.data.dao.MemberDAO;
import imlsw96.data.vo.MemberVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("msrv")
public class MemberService {
    @Autowired
    private MemberDAO mdao;

    // 회원 정보 생성
    public String newMember(){
        String result="회원 정보 생성 실패!";
        MemberVO mvo = new MemberVO();
        mvo.setUserid("spring4");
        mvo.setPassword("spring4");
        mvo.setName("이선우");
        mvo.setGrade("Gold");
        mvo.setPoint("1000");
        mvo.setRegdate("2021-01-18 10:55:00");

        if (mdao.insertMember(mvo)>0)
            result="회원 정보 생성 성공!";

        return result;
    }

    // 회원 정보 수정(이름, 등급, 포인트)
    public String modifyMember() {
        String result="회원정보 수정 실패";
        MemberVO mvo = new MemberVO();
        mvo.setUserid("spring4");
        mvo.setName("일지매");
        mvo.setGrade("Bronz");
        mvo.setPoint("0");

        if(mdao.updateMember(mvo)>0)
            result="회원정보 수정 성공!";
        return result;
    }

    // 회원정보 일지매 삭제
    public String removeMember() {
        String result="회원정보 삭제 실패!";
        MemberVO mvo = new MemberVO();
        mvo.setName("abc123");

        if(mdao.deleteMember(mvo)>0)
            result ="회원정보 삭제 성공!";
        return result;
    }

    // 회원정보 조회 (아이디,등급,가입일)
    public String readMember() {
        StringBuilder sb=new StringBuilder();
        String fmt="%10s %10s %10s\n";

        List<MemberVO> mvos = mdao.selectMember();
        for (MemberVO m : mvos){
            sb.append(String.format(fmt,m.getUserid(),m.getGrade(),m.getRegdate() ));
        }

        return sb.toString();

    }

    // 회원정보 조회 ( 아이디로 검색)
    public String readOneMember() {
        String result = "";
        String fmt = "%10s %10s %10s %10s %10s";

        MemberVO mvo = mdao.selectOneMember("abc123");

        result = String.format(fmt, mvo.getUserid(),mvo.getName(),mvo.getGrade(),mvo.getPoint(),mvo.getRegdate());

        return result;
    }
}

#MemberDAO

package imlsw96.data.dao;

import imlsw96.data.vo.MemberVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

@Repository("mdao")
public class MemberDAO {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    // MemberDAO 에서 사용할 JdbcTemplate 객체를
    // DI 받기 위해 @Autowired 로 선언

    // insert, update , delete 문 실행시
    // jdbcTemplate.update 메서드를 이용함
    public int insertMember(MemberVO mvo) {
        String sql="insert into member values(?,?,?,?,?,?)";

        // SQL문에 전달할 매개변수 값 선언
        Object[] params = new Object[] {
                mvo.getUserid(),mvo.getPassword(),mvo.getName(),
                mvo.getGrade(),mvo.getPoint(),mvo.getRegdate()
        };
        // SQL문에 전달할 매개변수 값들의 유형 (생략)
        return jdbcTemplate.update(sql,params);
    }


    public int updateMember(MemberVO mvo) {
        String sql = " update member set name = ?, grade = ?, point = ? where userid = ?";
        Object[] params = new Object[]{
                mvo.getName(), mvo.getGrade(), mvo.getPoint(), mvo.getUserid()};

        return jdbcTemplate.update(sql, params);

    }

    public int deleteMember(MemberVO mvo) {
        String sql="delete from member where name=?";


        return jdbcTemplate.update(sql,mvo);
    }

    // select all 문 실행시 jdbcTemplate.query 메서드를 이용함
    public List<MemberVO> selectMember() {
        String sql = "select userid, grade, regdate from member";
        RowMapper<MemberVO> mapper = new MemberOneRowMapper();

        return jdbcTemplate.query(sql,mapper);
    }

    //  select one 문을 실행시 jdbcTemplate.queryForObject 메서드를 이용함
    public MemberVO selectOneMember(String userid) {
        String sql = "select * from member where userid = ? limit 1";

        Object[] params = new Object[] {userid};
        RowMapper<MemberVO> mapper = new MemberRowMapper();
        // 콜백 클래스 등록만 하고, 호출/실행은 따로 하지 않음
        // 단, query 메서드가 실행하는 도중
        // rs.next가 참인 경우에 한해 IoC컨테이너가
        // mapper 객체의 mapRow 메서드를 호출함.

        return jdbcTemplate.queryForObject(sql,params,mapper);
   }


    // 회원정보를 출력하는 RowMapper 클래스
    private class MemberRowMapper implements RowMapper<MemberVO> {
        @Override
        public MemberVO mapRow(ResultSet rs, int num) throws SQLException {
            MemberVO mvo = new MemberVO();
            mvo.setUserid(rs.getString("userid"));
            mvo.setGrade(rs.getString("grade"));
            mvo.setRegdate(rs.getString("regdate"));
            return mvo;
        }
    }


    private class MemberOneRowMapper implements RowMapper<MemberVO> {
        @Override
        public MemberVO mapRow(ResultSet rs, int i) throws SQLException {
            MemberVO mvo = new MemberVO();
            mvo.setUserid(rs.getString("userid"));
            mvo.setName(rs.getString("name"));
            mvo.setGrade(rs.getString("grade"));
            mvo.setPoint(rs.getString("point"));
            mvo.setRegdate(rs.getString("regdate"));
            return mvo;
        }
    }
}

 

Chapter 2 : DBCP 활용

 

위의 기존 service 등 클래스는 동일하게 사용하고 xml에서만 DBCP를 활용해본다.

 

#spring4data02.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 어노테이션 기반 bean 설정-->
    <context:component-scan base-package="imlsw96.data.service, imlsw96.data.dao"/>

  

    <!--DBCP API : 대규모 -->
    <!--database connection pool-->
    <!--요청이 올때마다 connection 객체를 생성하는 것이 아니라-->
    <!--일정 수의 connection 객체를 미리 만들어 pool에 저장해 두고-->
    <!--요청이 발생하면 pool에서 connection 객체를 가져다 쓰게 하는 방식-->
    <!--connection 객체를 다 사용하면 바로 파괴하지 않고 pool에 반납 -->
    <bean id="dbcpDataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="org.mariadb.jdbc.Driver"/>
        <property name="url" value="jdbc:mariadb://mariadb.cw2h1nljbpsk.ap-northeast-2.rds.amazonaws.com:3306/playground"/>
        <property name="username" value="playground"/>
        <property name="password" value="playground2020"/>
    </bean>

    <!--JNDI API : 분산환경 -->

    <!-- 스프링 JDBC template 정의 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dbcpDataSource"/>
    </bean>
</beans>

 

Chapter 03 : mybatis를 이용하기.

 

MyBatis 의존성 추가.

#pom. xml

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>

mybatis?

spring JDBC API를 좀 더 편하게 사용하기 위해

개발된 Persitence 계층 프레임워크

자바 객체와 SQL문 사이의 자동 Mapping 기능을 지원

 

코드 생산성 증대 : JDBC 관련 코드를 60% 정도 줄여줌

hibernate나 JPA보다 익히기 쉬움

 

mybatis 주요 컴포넌트

SqlMapConfig.xml : 디비 접속 정보 등 환경설정 파일

SqlSessionFactoryBuilder : 설정 파일을 통해 mybatis 객체 생성

SqlSessionFactory : sqlSession 객체 생성

SqlSession : SQL 실행 또는 트랜잭션 관리 명령 실행

Mapping 파일 : SQL문과 OR Mapping 설정

 

 

#Spring4data03.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:P="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 어노테이션 기반 bean 설정-->
    <context:component-scan base-package="imlsw96.data.service, imlsw96.data.dao"/>

    <!--DBCP API : 대규모 -->
    <!--database connection pool-->
    <!--요청이 올때마다 connection 객체를 생성하는 것이 아니라-->
    <!--일정 수의 connection 객체를 미리 만들어 pool에 저장해 두고-->
    <!--요청이 발생하면 pool에서 connection 객체를 가져다 쓰게 하는 방식-->
    <!--connection 객체를 다 사용하면 바로 파괴하지 않고 pool에 반납 -->
    <bean id="dbcpDataSource" class="org.apache.commons.dbcp2.BasicDataSource"
          p:driverClassName="org.mariadb.jdbc.Driver"
        p:url ="jdbc:mariadb://mariadb.cw2h1nljbpsk.ap-northeast-2.rds.amazonaws.com:3306/playground"
        p:username ="playground"
        p:password ="playground2020"/>

    <!-- myBatis3 설정#1 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
        p:dataSource-ref="dbcpDataSource"
        p:mapperLocations="classpath:mybatis3/MemberMapper.xml"/>

    <!-- myBatis3 설정#2 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory" index="0"/>
    </bean>

    <!-- 스프링 JDBC template 정의 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dbcpDataSource"/>
    </bean>

</beans>

위의 코드에서 myBatis 설정을 보면 

sqlSessionFactoryBean을 이용해  스프링 컨테이너에 넣기 위해 bean으로 등록,

classpath값으로 Mapping 할 Mapper파일 위치 값을 준다.

그리고 sqlSessionTemplate를 이용해 위에 설정한 sqlSessionFactory를 참조하게 만든다. 

 

SqlSessionFactoryBean? 

mybatis에서는 SqlSession을 생성하기 위해 SqlSessionFactory를 사용

- 세션을 한번 생성하면 매핑 구문을 실행하거나 커밋 또는 롤백을 하기 위해 세션을 사용할 수 있다.

- 더 이상 필요하지 않은 상태가 되면 세션을 닫는다.

mybatis + Spring 연동 모듈(라이브러리)에서는 SqlSessionFactoryBean이 대신 사용된다.

연동 모듈을 사용하면 SqlSessionFactory를 직접 사용할 필요가 없다.

스프링 트랜잭션 설정에 따라 자동으로 커밋 혹은 롤백을 수행하고 닫히는 스레드에 안전한 SqlSession 개체가 

스프링 빈에 주입될 수 있다. 

 

SqlSessionTemplate?

SqlSessionTeamplate는 mybatis 스프링 연동 모듈의 핵심

SqlSessionTemplate는 SqlSession을 구현하고 코드에서 SqlSession을 대체하는 역할을 한다.

SqlSessionTemplate는 스레드에 안전하고 여러 개의 DAO나 매퍼에서 공유할 수 있다. 

 

#MemberMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="member">
    <insert id="insertMember" statementType="PREPARED"
        parameterType="imlsw96.data.vo.MemberVO">
        insert into member values(#{userid},#{password},#{name},#{grade},#{point},#{regdate})
    </insert>
    <update id="updateMember" statementType="PREPARED" parameterType="imlsw96.data.vo.MemberVO">
        update member set name = #{name}, grade = #{grade}, point = #{point} where userid = #{userid}
    </update>

    <delete id="deleteMember" statementType="PREPARED"
        parameterType ="String">
        delete from member where name=#{name}
    </delete>

    <select id="selectList" statementType="PREPARED" resultType="imlsw96.data.vo.MemberVO">
        select userid, grade, regdate from member
    </select>
    <select id="selectOne" statementType="PREPARED" resultType="imlsw96.data.vo.MemberVO">
        select * from member where userid = #{userid} limit 1
    </select>
</mapper>

위의 코드를 보면 쿼리문이 들어가 있는 걸 알 수 있다.

그리고 그 태그 이름에는 SQL과 익숙한

DML인 insert, delete update와

DCL인 select가 들어가 있는 걸 확인할 수 있다. 

 

그리고 생소할 수 있는 statementType과 resultType 그리고 parameterType 등을 추가로 설정하는 걸 볼 수 있는데

 

mybatis를 사용 안 하고 java.sql을 사용하여 데이터 베이스를 조회할 때를 생각해보자.

Connection으로 데이터베이스와 연결하고

PreparedStatement로 쿼리를 요청하고

ResultSet으로 쿼리 한 결과를 받아오도록 사용한다. 

 

위의 역할들을 해주기 위해서

statementType에 쿼리문을 읽어오기 위해 PREPARED를 사용하여 쿼리문을 불러오고 

parameterType은 매개변수를 설정해주는 것. DB의 칼럼명과 동일한 VO클래스로 설정해준다. 

*delete 기능에서는 하나의 값만 가져오면 되기에 String

 

select기능을 담당할 때는 resultType을 추가로 설정하는 걸 확인할 수 있는데 

resultType은 mapper 쿼리로 가져온 결과를 java의 어떤 타입으로 변환하여 반환할지 정해줘야 한다.

VO클래스의 멤버 변수들로 반환해서 읽어오게 해야 하기 때문에 VO클래스로 설정한다.

 

#MemberService03

package imlsw96.data.service;


import imlsw96.data.dao.MemberDAO03;
import imlsw96.data.vo.MemberVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("msrv03")
public class MemberService03 {
    @Autowired
    private MemberDAO03 mdao03;

    // 회원 정보 생성
    public String newMember(){
        String result="회원 정보 생성 실패!";
        MemberVO mvo = new MemberVO();
        mvo.setUserid("spring4");
        mvo.setPassword("spring4");
        mvo.setName("이선우");
        mvo.setGrade("Gold");
        mvo.setPoint("1000");
        mvo.setRegdate("2021-01-18 10:55:00");

        if (mdao03.insertMember(mvo)>0)
            result="회원 정보 생성 성공!";

        return result;
    }

    // 회원 정보 수정(이름, 등급, 포인트)
    public String modifyMember() {
        String result="회원정보 수정 실패";
        MemberVO mvo = new MemberVO();
        mvo.setUserid("spring4");
        mvo.setName("일지매");
        mvo.setGrade("Bronz");
        mvo.setPoint("0");

        if(mdao03.updateMember(mvo)>0)
            result="회원정보 수정 성공!";
        return result;
    }

    // 회원정보 일지매 삭제
    public String removeMember() {
        String result="회원정보 삭제 실패!";
        MemberVO mvo = new MemberVO();
        mvo.setName("abc123");

        if(mdao03.deleteMember(mvo)>0)
            result ="회원정보 삭제 성공!";
        return result;
    }

    // 회원정보 조회 (아이디,등급,가입일)
    public String readMember() {
        StringBuilder sb=new StringBuilder();
        String fmt="%10s %10s %10s\n";

        List<MemberVO> mvos = mdao03.selectMember();
        for (MemberVO m : mvos){
            sb.append(String.format(fmt,m.getUserid(),m.getGrade(),m.getRegdate() ));
        }

        return sb.toString();

    }

    // 회원정보 조회 ( 아이디로 검색)
    public String readOneMember() {
        String result = "";
        String fmt = "%10s %10s %10s %10s %10s";

        MemberVO mvo = mdao03.selectOneMember("abc123");

        result = String.format(fmt, mvo.getUserid(),mvo.getName(),mvo.getGrade(),mvo.getPoint(),mvo.getRegdate());

        return result;
    }
}

 

 

서비스 클래스의 경우 늘 짜 오듯이 자바스럽게 짜고 DB에 처리해야 할부분은 DAO에 넘겨준다. 

 

#MemberDAO03

package imlsw96.data.dao;

import imlsw96.data.vo.MemberVO;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;


import java.util.List;

@Repository("mdao03")
public class MemberDAO03 {
    @Autowired
    private SqlSession sqlSession;
    // mybatis 사용하기 위해
    // SqlSession 객체를 MemberDAO03에 DI함


    public int insertMember(MemberVO mvo) {

        return sqlSession.insert("member.insertMember",mvo);
    }


    public int updateMember(MemberVO mvo) {

        return sqlSession.update("member.updateMember",mvo);

    }

    public int deleteMember(MemberVO mvo) {

        return sqlSession.update("member.deleteMember",mvo);
    }


    public List<MemberVO> selectMember() {


        return sqlSession.selectList("member.selectList");
    }


    public MemberVO selectOneMember(String userid) {

        return sqlSession.selectOne("member.selectOne",userid);
   }



    }


 

 

DAO클래스의 코드들을 보면 확연히 깔끔해진 걸 확인할 수 있다.

Mapper에 쿼리문등 설정을 해주고 DAO클래스에서는 sqlSession 객체를 생성해서 

설정해둔 Mapper기능들을 불러오면 된다. 

 

java.jdbc로만 짠 코드들

2020/12/17 - [JAVA & APP :국비지원 학원 복습/JAVA] - JDBC를 이용한 사원 정보 프로그램 만들어보기.

 

JDBC를 이용한 사원정보 프로그램 만들어보기.

학원에서 자바와 mysql을 맛보기(?) 식으로 공부를 어느정도 진행되고 오늘 JDBC를 이용해 성적프로그램을 간단하게 만들어 보았고 혼자 실습(?)하는 느낌으로 사원정보 프로그램을 만들어보려고

i-am-lsw.tistory.com

참고한 링크들

linked2ev.github.io/mybatis/2019/09/08/MyBatis-4-DAO%EC%99%80-SqlSessionTemplate,-SqlSessionDaoSupport/

 

[MyBatis] DAO와 SqlSessionTemplate, SqlSessionDaoSupport

MyBatis3에서 SqlSession 과 SqlSessionTemplate, SqlSessionDaoSupport 개념설명 그리고 mybatis-spring 연동모듈을 사용해서, @Repository(애너테이션)을 선언해 DAO객체를 생성해서 DB에 접근하는 방법

linked2ev.github.io

hychul.github.io/development/2019/05/23/mybatis-resulttype-list/

 

MyBatis의 resultType 정리

MyBatis의 select 태그에서 사용되는 resultType 프로퍼티는 mapper 쿼리로 가져온 결과를 Java의 어떤 타입으로 변환하여 반환할지 지정하는 프로퍼티입니다. 일단적으로 java.util.Map과 같이 패키지를 포

hychul.github.io

aljjabaegi.tistory.com/402

 

JDBC, DBCP란? 웹 어플리케이션의 DB접속에 대한 고찰

JDBC, DBCP란? 웹 어플리케이션의 DB접속에 대한 고찰 자바 웹 어플리케이션에서 DB 접속과 관련된 라이브러리가 JDBC, DBCP 입니다. JDBC는 Java DataBase Connectivity 의 약자로 자바에서 데이터베이스에 연

aljjabaegi.tistory.com

khj93.tistory.com/entry/MyBatis-MyBatis%EB%9E%80-%EA%B0%9C%EB%85%90-%EB%B0%8F-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC

 

[MyBatis] MyBatis란? 개념 및 데이터구조

MyBatis란? 객체 지향 언어인 자바의 관계형 데이터베이스 프로그래밍을 좀 더 쉽게 할 수 있게 도와 주는 개발 프레임 워크로서 JDBC를 통해 데이터베이스에 엑세스하는 작업을 캡슐화하고 일반 S

khj93.tistory.com

 

반응형
반응형

2020-01-24

 

Chapter 01 원초적인 코드로 짜 보기. 

상품 등록 프로그램V1

HelloSpring4 App08 -> ProductService01 -> ProductDAO01

Main 클래스에서 상품등록 기능을 실행하기 위해 

new 연산자를 이용해서 ProductService01의 객체를 만들고

그 객체를 통해 newProduct()를 호출함. 

 

DAO는 실제로 DB에 연결하지 않고 어떤 식으로 작동되는지 실험하기 위해 

간략하게 표현. 

 

#HelloSpring4 App08

package imlsw96.basic;

import imlsw96.product.ProductService01;



public class HelloSpring4App08 {

    public static void main(String[] args) {
        ProductService01 ps = new ProductService01 ();
        ps. newProduct();
    }

#ProductService01

package imlsw96.product;

public class ProductService01 {

    // 상품등록 기능을 수행하는 메서드
    public void newProduct() {
        System.out.println("새로운 상품을 등록합니다!");


        ProductVO pvo = new ProductVO();
        pvo.setPname("수지로션");
        pvo.setPrice(35000);

        // 입력받은 상품 정보에 영속성을 부여하기 위해 DAO 호출
        // 마찬가지로 new 연산자로 해당 객체를 생성하고
        // insertProduct 메서드 호출함
        ProductDAO01 pdao = new ProductDAO01();
        pdao.insertProduct(pvo);

        System.out.println("새로운 상품이 등로되었습니다 !");
    }
}

#ProductDAO01

package imlsw96.product;

public class ProductDAO01 {
    public void insertProduct(ProductVO pvo) {
        System.out.printf(
                "입력하신 %s가 성공적으로 저장되었어요\n",pvo.getPname() );
    }
}

#ProductVO

package imlsw96.product;

public class ProductVO {
    private String pname;
    private  int price;
    public void setPname(String pname) {
        this.pname = pname;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public String getPname() {
        return pname;
    }

    public int getPrice() {
        return price;
    }
}

스프링 프레임워크를 사용하지 않고 작성할 수 있는 원초적인 코드?라고 보면 된다. 

클래스별 기능을 분리해 메인클래스에서 서비스의 제품 등록 기능을 구현하고 

등록할 제품을 DAO클래스로 넘겨서 DB에 저장하는 처리를 해줌. 

 

 

Chapter 02 인터페이스를 기반으로 코드를 재작성해보기. 

상품 등록 프로그램 V2

HelloSpring4 App09 -> ProductService02 -> ProductDAO02

 

#HelloSpring4 App09

package imlsw96.basic;

import imlsw96.product.ProductService02;
import imlsw96.product.ProductService02Impl;


public class HelloSpring4App09 {


    public static void main(String[] args) {
        ProductService02 ps = new ProductService02Impl();
        ps. newProduct();
    }
}

 

#ProductService02 (Interface) , ProductDAO02 (Interface)

package imlsw96.product;

public interface ProductService02 {


     void newProduct();
}
package imlsw96.product;

public interface ProductDAO02 {
     void insertProduct(ProductVO pvo);
}

#ProductService02 Impl , ProductDAO02 Impl (인터페이스 상속 후 메서드 오버 라이딩)

package imlsw96.product;

public class ProductService02Impl implements ProductService02{

    // 상품등록 기능을 수행하는 메서드
    @Override
    public void newProduct() {
        System.out.println("새로운 상품을 등록합니다!");


        ProductVO pvo = new ProductVO();
        pvo.setPname("수지로션");
        pvo.setPrice(35000);

        // 입력받은 상품 정보에 영속성을 부여하기 위해 DAO 호출
        // 영속성이란 ? : 데이터가 영구히 저장되게끔
        // 마찬가지로 new 연산자로 해당 객체를 생성하고
        // insertProduct 메서드 호출함
        ProductDAO02 pdao = new ProductDAO02Impl();
        pdao.insertProduct(pvo);

        System.out.println("새로운 상품이 등로되었습니다 !");
    }
}
package imlsw96.product;

public class ProductDAO02Impl implements ProductDAO02 {

    @Override
    public void insertProduct(ProductVO pvo) {
        System.out.printf(
                "입력하신 %s가 성공적으로 저장되었어요\n",pvo.getPname() );
    }
}

#VO클래스 위와 동일

 

인터페이스만 추가했고 Chapter 01의 경우와 크게 다르지 않다. 

Service와 DAO 클래스를 인터페이스 클래스를 만들고 그걸 상속시킨 Impl 클래스들을 객체 생성해서 

메서드를 실행시켰다. 

 

 

Chapter 03 스프링 프레임워크를 이용해 보기.

 

상품 등록 프로그램 V3

 

HelloSpring4 App10 -> ProductService03 Impl -> ProductDAO03 Impl

 

Main 클래스에서 상품등록 기능을 실행하기 위해 

스프링 컨테이너가 만들어준 객체를 이용함

이때 setter 메서드를 이용해서 객체를 주입 받음

bean 설정 파일에는 property라는 태그에 둠 

 

# product.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="ps03" class="imlsw96.product.ProductService03Impl">
        <property name="pdao" ref="pdao"/>
    </bean>
    <bean id="pdao" class="imlsw96.product.ProductDAO03Impl" />

</beans>

클래스 호출 패턴 글에서 06번 예제를 보면 알 수 있듯이. xml파일에 스프링 컨테이너가 미리 만들어놓을 객체를

설정해준다. ps03이라는 id를 가지는 ProductService03 Impl 서비스 객체를 만들고 

서비스 클래스에서 DAO클래스로 넘겨줘야 하니 DAO클래스 객체가 필요 그래서 pdao라는 이름을 가진 property속성을

추가해주고 pdao속성은 pdao라는 id를 가진 DAO03 Impl 객체를 bean을 통해 만들어준다. 

 

#HelloSpring4 App10

package imlsw96.basic;

import imlsw96.product.ProductService02;
import imlsw96.product.ProductService02Impl;
import imlsw96.product.ProductService03Impl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class HelloSpring4App10 {

 public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("product.xml");

        ProductService02 ps = (ProductService03Impl) ctx.getBean("ps03");

        // ProductDAO03 pdao= new ProductDAO03Impl()
        // ps.setPdao(pdao);
        // ProductDAO03에 대한 객체를 사용하려면 new 연산자로 객체를 생성해야 하는데
        // 이러한 작업을 스프링 컨테이너에 의해 대신 처리함
        // 단, bean.xml에 이러한 내용이 미리 선언되어 있어야 함
        //
        ps.newProduct();
    }
}

 

 

메인 메서드에서 ApplicationContext를 이용해서 product.xml을 읽어와? 객체 생성을 스프링컨테이너에게 맡기고

객체생성 코드를 생략할 수 있다. getBean을 이용하여  

 

#ProductService , ProductDAO 인터페이스는 위 Chapter와 동일

 

# ProductService03 Impl, ProductDAO03 Impl 

package imlsw96.product;

public class ProductService03Impl implements ProductService02{

    private ProductDAO02 pdao;

    public void setPdao(ProductDAO02 pdao) {
        this.pdao = pdao;
    }

    // 상품등록 기능을 수행하는 메서드
    @Override
    public void newProduct() {
        System.out.println("새로운 상품을 등록합니다!");


        ProductVO pvo = new ProductVO();
        pvo.setPname("수지로션");
        pvo.setPrice(35000);

        // 입력받은 상품 정보에 영속성을 부여하기 위해 DAO 호출
        // 영속성이란 ? : 데이터가 영구히 저장되게끔
        // 마찬가지로 new 연산자로 해당 객체를 생성하고
        // insertProduct 메서드 호출함
       // ProductDAO02 pdao = new ProductDAO03Impl();
        pdao.insertProduct(pvo);

        System.out.println("새로운 상품이 등로되었습니다 !");
    }
}

 

 인터페이스 기반으로 코드 작성 스프링에 의해 객체를 주입받는 방법은 setter를 이용하거나 생성자를 이용하는 것

 

 

Chapter 04 스프링 어노테이션 사용해서 만들어보기.

 

어노테이션을 사용해서 클래스를 만들기 전에 어노테이션(Annotation)에 간단히 알아보고 가자. 

1. Annotation이란? 

@를 이용한 주석, 자바 코드에 주석을 달아 특별한 의미를 부여한 것. 

메타데이터(실제 데이터가 아닌 Data를 위한 데이터)라고도 불리고 JDK5부터 등장

컴파일러가 특정 오류를 억제하도록 지시하는 것과 같이 프로그램 코드의 일부가 아닌 

프로그램에 관한 데이터를 제공, 코드에 정보를 추가하는 정형화된 방법. 

 

2. Annotation이 나온 이유 

프로그램의 규모가 방대해지면서 XML이 가지는 설정 정보의 양이 많아지기 때문

-Annotation을 사용하면 직관적인 메타데이터의 설정 가능하다 소스코드와 함께 쓰이기 때문에 

 

3. Annotation 사용 시 장점 :

데이터에 대한 유효성 검사 조건을 어노테이션을 사용하여 Model 클래스에 직접 명시함으로써

해당 데이터들에 대한 유효 조건을 쉽게 파악할 수 있게 되며 , 코드의 양도 줄어들게 된다. 

 

#Product12. xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!--    <bean id="ps03" class="imlsw96.product.ProductService03Impl">-->
<!--        <property name="pdao" ref="pdao"/>-->
<!--    </bean>-->
<!--    <bean id="pdao" class="imlsw96.product.ProductDAO03Impl" />-->

    <context:component-scan base-package="imlsw96.product.anno"/>

</beans>

기존과 같이 작성했더라면 주석 부분처럼 작성해야겠지만 component-scan을 이용하면 지정 패키지 (경로)에 있는 모든 클래스를 가져올 수 있다. 

 

 

보다시피 DAO03 Impl과 Service03 Impl파일을 가져온다. 

#ProductDAO03 Impl

package imlsw96.product.anno;

import imlsw96.product.ProductDAO02;
import imlsw96.product.ProductVO;
import org.springframework.stereotype.Component;

@Component("pdao")
public class ProductDAO03Impl implements ProductDAO02 {

   @Override
    public void insertProduct(ProductVO pvo) {
        System.out.printf(
                "입력하신 %s가 성공적으로 저장되었어요\n",pvo.getPname() );
    }
}

#Service03 Impl

package imlsw96.product.anno;

import imlsw96.product.ProductDAO02;
import imlsw96.product.ProductService02;
import imlsw96.product.ProductVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("ps03")
public class ProductService03Impl implements ProductService02 {

    @Autowired
    private ProductDAO02 pdao;
    // 이전 예제에서는 setter 메서드를 통해 DI 되었음
    // 지금은 어노테이션을 선언해서 setter 메서드 정의 없이
    // DI 받음

//    public void setPdao(ProductDAO02 pdao) {
//        this.pdao = pdao;
//    }

    // 상품등록 기능을 수행하는 메서드
    @Override
    public void newProduct() {
        System.out.println("새로운 상품을 등록합니다!");


        ProductVO pvo = new ProductVO();
        pvo.setPname("수지로션");
        pvo.setPrice(35000);

        // 입력받은 상품 정보에 영속성을 부여하기 위해 DAO 호출
        // 영속성이란 ? : 데이터가 영구히 저장되게끔
        // 마찬가지로 new 연산자로 해당 객체를 생성하고
        // insertProduct 메서드 호출함
       // ProductDAO02 pdao = new ProductDAO03Impl();
        pdao.insertProduct(pvo);

        System.out.println("새로운 상품이 등로되었습니다 !");
    }
}

#HelloSpring4 App12

package imlsw96.basic;


import imlsw96.product.ProductService02;
import imlsw96.product.anno.ProductService03Impl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class HelloSpring4App12 {
    // 상품 등록 프로그램v4
    // HelloSpring4App10 -> ProductService02 -> ProductDAO02

    // Main 클래스에서 상품등록기능을 실행하기 위해
    // 스프링 컨테이너가 만들어준 객체를 이용함
    // 이때 setter 메서드를 이용해서 객체를 주입받음
    // bean 설정 대신 간단하게 어노테이션으로 DI를 함
    //




    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("product12.xml");

        ProductService02 ps = (ProductService03Impl) ctx.getBean("ps03");
        
        ps.newProduct();
    }
}

보다시피 xml파일의 코드가 간단해졌고 어노테이션을 달아 다른 클래스의 코드도 전보다 좀 더? 나아진 걸 확인할 수 있다. 

 

ApplicationContext 등 스프링 관련 정리

jaehun2841.github.io/2018/10/21/2018-10-21-spring-context/#application-context

 

Application-Context와 Servlet-Context | Carrey`s 기술블로그

들어가며 회사 업무 중에 AOP를 이용하여 개발 중에 AOP가 제대로 설정이 되지 않는 문제가 있었다. 문제의 원인은 Component-scan 위치에 따른 Bean 생성 위치에 있었다. core가 되는 프로젝트는 applicatio

jaehun2841.github.io

 

Annotation에 관한 정리

palyoung.tistory.com/72

 

# Spring Annotation(어노테이션)이란?

# Annotation이란? - @를 이용한 주석, 자바코드에 주석을 달아 특별한 의미를 부여한 것   (참고로 클래스, 메소드, 변수 등 모든 요소에 선언이 가능) - 메타데이터(실제데이터가 아

palyoung.tistory.com

 

반응형
반응형

 


1. 첫 번째 사례

클래스 내부에 메서드 생성

 

간단한 인삿말을 출력하는 프로그램 작성

메시지를 출력하는 sayHello메서드를 만들어 호출

단, sayHello메서드를 호출하려면

먼저 HelloSpring4App01에 대한 객체를 생성해야 함

 

인사말을 출력하는 메서드가 현재 클래스에 있기 때문에

프로그램 확장성 측면에서 봤을 때 제약이 존재 

ex) 참조하는 메서드의 인자가 1개에서 2개가 된다면? 

즉, 유지보수/업무분담의 어려움이 발생

 

해결책 : 인사말 출력하는 기능을 담당하는 별도의 클래스 생성

public class HelloSpring4App01 {
    public static void main(String[] args) {
        //System.out.println("Hello, World!!");
        HelloSpring4App01 app = new HelloSpring4App01(); //2b
        app.sayHello("sunwoo!");

    }

    private void sayHello(String msg) { //2a
        System.out.println("Hello," + msg);
    }
}

 


2. 두 번째 사례

외부 클래스를 통해 인사말을 구현하는 기능 구현

인삿말을 출력하려면 외부 클래스를 객체화한 뒤 

sayHello 메서드 호출

 

한편 HelloSpring4 App02을 실행하려면

HelloSpring4 Bean02라는 클래스가 있어야 함

즉, 클래스 간의 의존성이 생긴다

의존성이 커짐으로 발생하는 부수적인 단점은

코드 변경 시 그것과 연관되는 다른 클래스에도 영향을 미침

유지보수의 범위가 넓어짐 

 

해결책 : 인터페이스를 도입해서 객체 간 의존성을 낮춤

// HelloSpring4 App02

public class HelloSpring4App02 {
    public static void main(String[] args) {
        HelloSpring4Bean02 bean = new HelloSpring4Bean02();
        bean.sayHello("Sunwoo!!!");
    }
}

// HelloSpring4 Bean02

package imlsw96.bean;

public class HelloSpring4Bean02 {

    public void sayHello(String world) {
        System.out.println("Hello," + world);
    }
}

03. 세 번째 사례

인사말을 한국어, 영어, 일본어로 출력하도록 기능을 개선.

해당 기능을 제각각의 이름으로 호출하는 경우 

지나치게 복잡해지고 기능에 따라 이름을 

일일이 외워야 하는 불편함이 존재함 

 

해결 : 인터페이스를 도입해서 객체 간 의존성을 낮춤

public class HelloSpring4App03 {


    public static void main(String[] args) {
        HelloSpring4Bean03 bean = new HelloSpring4Bean03();
        bean.sayHelloKor("스프링4 !!!");
        bean.sayHelloEng("spring4 !!!");
        bean.sayHelloJpn("일본어 !!!");
    }
}
package imlsw96.bean;

public class HelloSpring4Bean03 {

    // 다양한 언어로 인삿말을 출력하는 메서드
    public void sayHelloKor(String world) {
        System.out.println("안녕하세요," + world);
    }

    public void sayHelloEng(String world) {
        System.out.println("Hello," + world);
    }

    public void sayHelloJpn(String world) {
        System.out.println("おはようございます," + world);
    }
}

 


4. 네 번째 사례

인터페이스를 도입 

 

확장성이 높고 유연한 코드를 작성할 수 있음

또한, 객체 간의 tightly coupled도 피할 수 있음

03번의 예로 객체 생성할 때 그에 맞는 클래스명, 다른 변수명으로 객체를 생성해서 사용해야 하지만

객체 간의 의존성도 낮출 수 있음 

 

하지만, new 연산자로 객체를 생성하는 코드가 노출

다시 말해, HelloSpring4 Bean04는 

HelloSpirng4 Bean04 Kor.HelloSpirng4 Bean04 Eng, HelloSpirng4 Bean04 Jpn에 의존적이라는 의미

 

해결 : factory 패턴을 이용해서 

객체 생성을 전담하는 클래스를 만들어

객체 생성과정을 캡슐화함

 

package imlsw96.basic;


import imlsw96.bean.*;

public class HelloSpring4App04 {



    public static void main(String[] args) {
        HelloSpring4Bean04 bean = new HelloSpring4Bean04Kor();
        bean.sayHello("스프링4");

        bean = new HelloSpring4Bean04Eng();
        bean.sayHello("Spring4");

        bean = new HelloSpring4Bean04Jpn();
        bean.sayHello("Spring4");
    }
}
package imlsw96.bean;

public interface HelloSpring4Bean04 {
    // 다국어 인삿말을 위해 동일한 인터페이스를 정의
    void sayHello( String msg );
}
package imlsw96.bean;

public class HelloSpring4Bean04Eng implements HelloSpring4Bean04 {

    // 영어로 인삿말을 출력하는 메서드
    @Override
    public void sayHello(String world) {
        System.out.println("Hello," + world);
    }


}
package imlsw96.bean;

public class HelloSpring4Bean04Jpn implements HelloSpring4Bean04 {

    // 일본어로 인삿말을 출력하는 메서드
    @Override
    public void sayHello(String world) {
        System.out.println("こんにちは," + world);
    }


}
package imlsw96.bean;

public class HelloSpring4Bean04Kor implements HelloSpring4Bean04 {

    // 한국어HelloSpring4Bean03로 인삿말을 출력하는 메서드
    @Override
    public void sayHello(String world) {
        System.out.println("안녕하세요," + world);
    }


}

 


5. 다섯 번째 사례

factory 패턴을 이용해서 객체 생성을 캡슐화함

한편, 매개변수를 이용해서 생성할 객체를 취사선택함

 

객체생성을 팩토리 패턴으로 구현해야 함

따라서, 개발자가 신경 써야 할 부분이 은연중에 추가됨

즉, 비즈니스 로직 코드 작성하는 것도 버거운데

객체 생성 관련 클래스로 따로 작성하는 것은 더욱 힒듬 

 

해결 : 객체 생성 부분은 외부의 힘을 빌림

IoC 컨테이너가 객체를 생성하고 그것을 주입 (inject) 받음

import imlsw96.bean.*;

public class HelloSpring4App05 {

    public static void main(String[] args) {
        HelloSpring4Bean05Factory.create("kor").sayHello("스프링4");
        HelloSpring4Bean05Factory.create("eng").sayHello("스프링4");
        HelloSpring4Bean05Factory.create("jpn").sayHello("스프링4");
    }
}
package imlsw96.bean;

public class HelloSpring4Bean05Factory {


    // 인삿말을 출력하는 객체를 생성하는 create 메서드 정의
    // type이라는 매개변수를 통해 출력할 인삿말의 유형을 선택함

    public static HelloSpring4Bean04 create(String type){
        HelloSpring4Bean04 bean = null;

        if (type.equalsIgnoreCase("kor"))
            bean = new HelloSpring4Bean04Kor();
        else if (type.equalsIgnoreCase("eng"))
            bean = new HelloSpring4Bean04Eng();
        else if (type.equalsIgnoreCase("jpn"))
            bean = new HelloSpring4Bean04Jpn();

        return bean;
    }

}

이 부분은 보면서 헷갈릴 수도 있어서 그냥 필자의 뇌피셜을 싸질러보면

위의 4번째 사례에서 만들어놓은 interface타입을 return 하는 05 Factory 클래스에 create라는 이름의 메서드를 생성해서 

인자 값 검사를 해줘서 인터페이스를 상속받아 kor , eng, jpn클래스를 인스턴스화를 시켜주고 그 값을 넘겨준다. 

그러고 메인 메서드가 있는 App05번에 보면 Factory클래스에 생성해놓은 메서드를 이용하여 객체 생성 부분을 캡슐화해서 나라별로 언어를 실행하는 걸 확인할 수 있다. 


6. 스프링 프레임워크를 사용하는 첫번째 사례

05번의 에제를 보면 factory 패턴을 이용해서 객체를 생성하는 코드를 작성했었다. 

하지만 스프링프레임워크를 사용하면 이러한 과정은 필요 없다.

 

스프링 프레임워크를 이용한 개발의 필수요건은

인터페이스 기반 설계!

 

실행 원리 

예제를 보면 알 수 있듯 객체 생성 시 NEW연산자를 사용하지 않고

스프링이 대신 객체를 생성하고 프로그램에서는 DI를 통해 사용함

  1.  bean.xml에 스프링 컨테이너가 미리 생성해둬야 할 객체와 이름을 정의해 둠
  2.  프로그램이 시작되면 bean06.xml에 작성한 객체 정보를 스프링 컨테이너가 읽어서 객체를 만들어 둠
  3.  BeanFactory에서 getBean 메서드로 해당 객체를 가져와서 HelloSpring4 Bean04 타입의 변수에 주입
  4.  주입된 변수를 통해 sayHello메서드를 호출하면 인사말이 출력됨 

/bean06.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="kor" class="imlsw96.bean.HelloSpring4Bean04Kor"/>
    <bean id="eng" class="imlsw96.bean.HelloSpring4Bean04Eng"/>
    <bean id="jpn" class="imlsw96.bean.HelloSpring4Bean04Jpn"/>

</beans>

<bean>을 해서 클래스를 설정해주고 참조할 id 값을 설정해준 후 메인클래스로 돌아와서 확인해보자

 

/HelloSpring4 App06

package imlsw96.basic;

import imlsw96.bean.HelloSpring4Bean04;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class HelloSpring4App06 {

    public static void main(String[] args) {

        // 스프링 컨테이너의 관리를 받는 객체들이 정의된 설정파일 읽기
        BeanFactory bf = new XmlBeanFactory(
                new FileSystemResource("src/main/java/bean06.xml"));


        // 스프링 컨테이너를 통해 지정한 객체를 주입받음
        HelloSpring4Bean04 bean= (HelloSpring4Bean04) bf.getBean("kor");
        bean.sayHello("스프링4");

        bean=(HelloSpring4Bean04) bf.getBean("eng");
        bean.sayHello("스프링4");


        bean=(HelloSpring4Bean04) bf.getBean("jpn");
        bean.sayHello("스프링4");
    }
}

스프링 프레임워크에서 제공하는 컨테이너는 2가지 

BeanFactory / ApplicationContext 

이둘중 BeanFactory를 이용해서 bean06.xml 파일을 읽어왔는데 

BeanFactory 방식은 스프링에서 구식 방법이라 고함 

다음 사례에서는 ApplicationContext를 이용한 방법

 

7. 스프링 프레임워크를 사용하는 두 번째 사례

package imlsw96.basic;

import imlsw96.bean.HelloSpring4Bean04;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;


public class HelloSpring4App07 {


    public static void main(String[] args) {

        // 스프링 컨테이너의 관리를 받는 객체들이 정의된 설정파일 읽기
        ApplicationContext ctx = new
        FileSystemXmlApplicationContext("/src/main/java/bean06.xml");

        // 스프링 컨테이너를 통해 지정한 객체를 주입받음
        HelloSpring4Bean04 bean= (HelloSpring4Bean04) ctx.getBean("kor");
        bean.sayHello("스프링4");

        bean=(HelloSpring4Bean04) ctx.getBean("eng");
        bean.sayHello("스프링4");


        bean=(HelloSpring4Bean04) ctx.getBean("jpn");
        bean.sayHello("스프링4");
    }
}

/bean06.xml 동일

atoz-develop.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-XML-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC

 

[Spring] 스프링 XML 설정 파일 작성 방법 정리

[Spring] 스프링 XML 설정 파일 작성 방법 정리 📄 목차 1. 스프링 XML 설정 파일 포맷 - 기본 포맷 - 애노테이션 설정을 사용하기 위한 포맷 2. 빈(Bean) 설정 예시 - 자동 주입 설정 - autowire 속성 3.

atoz-develop.tistory.com

 

반응형
반응형

2021-01-21 새벽

수업내용 정리

시작하기 전 Maven에 대한 설명을 추가.

 


1. 빌드란? 

소스코드 파일을 컴퓨터에서 실행할 수 있는 독립 소프트웨어 가공물로 변환하는 과정 또는 그에 대한 결과이를 좀 더 쉽게 풀어 말하자면 우리가 작성한 소스코드(java), 

프로젝트에서 쓰인 각각의 파일 및 자원 등(. xml. jpg. jar. properties)을 

JVM이나 톰캣같은 WAS가 인식할 수 있는 구조로 패키징 하는 과정 및 결과물.

 

2. 빌드도구(Build tool) 

프로젝트 생성, 테스트 빌드, 배포 등의 작업을 위한 전용 프로그램

빠른 기간 동안 계속해서 늘어나는 라이브러리 추가, 프로젝트를 진행하며 라이브러리의 버전 동기화 어려움 해소를 위함

대표

대표적으로 maven, Ant, Gradle이 있다고 함.

 

3. Maven 정의 및 특징

Maven은 자바용 프로젝트 관리 도구로 Apache Ant의 대안으로 만들어짐.

프로젝트의 전체적인 라이프 사이클을 관리하는 도구

필요한 라이브러리를 특정 문서(pom.xml)에 정의해 놓으면 내가 사용할 라이브러리뿐만 아니라 

해당 라이브러리가 작동하는데에 필요한 다른 라이브러리들까지 관리하여 네트워크를 통해서

자동으로 다운받아 준다. 

 


반응형
반응형

2021-01-21 새벽

어려움에 이해가 안돼 흘러넘기던 아니 피하던 수업내용들을 뒤늦게나마 다시 조져보려고 쓰는 글

 


웹 애플리케이션

업무에서의 사용을 전제로, 인터넷을 기반으로 웹브라우저를 사용해서 여러 사용자가 데이터베이스에 접근해서 정보를 읽거나 저장 또는 수정, 삭제할 수 있도록 만들어진 응용프로그램을 의미 

 

 

웹 시스템의 기본적인 구조에서 정적콘텐츠클라이언트 머신의 웹 브라우저가 네트워크에 있는 웹 서버(정적 콘텐츠를 저장하는 서버)로부터 요청한 HTML을 읽어와 표시 

 

반면, 동적 콘텐츠웹서버에서 애플리케이션 서버(웹 서버의 요구에 따라서 콘텐츠를 동적으로 생성하는 서버)에 처리를 요청하고 대부분은 RDB에서 데이터를 읽어오거나 가공하고 그 처리 결과를 웹 서버에서 받아 웹브라우저에 표시

 

 

 

웹기술은 처음에 정적 콘텐츠만을 표시하는 기술 

정적 콘텐츠만으로는 웹기술을 업무에 이용하기에는 부족 

 

그래서 등장한 CGI( Common Gateway Interface) 기술로 동적 컨텐츠 반환 가능 시작 

but 요청할때마다 프로그램 실행 / 세션관리 부재 / 페이지생성로직 비즈니스 로직 분리불가 

 

CGI대체를 위한 JSP,Servlet 등장

멀티스레드로 실행 / 세션관리 / JSP로 페이지 생성 Servlet으로 비즈니르 로직 처리하는 아키텍쳐 지원

but  복잡한 애플리케이션 제작하는데는 여전히 어려움

 

-멀티스레드 :

일반적으로 하나의 프로세스는 하나의 스레드를 가지고 작업을 수행.

하지만 멀티스레드란 하나의 프로세스 내에서 둘 이상의 스레드가 동시에 작업을 수행하는 것을 의미

 

goodgid.github.io/What-is-Multi-Thread/

 

멀티 쓰레드(Multi Thread)란 무엇인가?

Index

goodgid.github.io

 

 

 

해결을 위한 EJB의 등장

기업규모의 시스템을 구축하는데 필요한 '컴포넌트들을 위한 구조물'

+ 기업형 (대규모) 분산 객체 시스템을 구축하는데 필요한 Java 표준기술

but 애초부터 분산용이므로 원격액세스만 지원 웹애플리케이션은 분산처리를 거의 사용X 로컬액세스만 주로 사용

+EJB 컨테이너에 의존하는 EJB속도가 느렸고 테스트하기 어렵다는 문제 지적 

이로인해 마틴 파울러는 EJB에 반발해 오래된 방식의 "간단한 자바 오브젝트로 돌아가자"는 말을 했고 

이는 POJO(plain Old Java Object)라는 용어의 기원이 됨

이러쿵저러쿵한 불편함의 역사로 지금의 스프링 등 여러 프레임워크가 생김

 

 

웹 애플리케이션의 아키텍처 :

일반적으로 애플리케이션 전체의 구조, 공통된 방식(메커니즘)이라고 정의 가능

시스템의 애플리케이션이 공통적으로 이용할 수 있는 사용자 인터페이스 구조나 데이터베이스 접근 방식 등 시스템의 기반이 되는 부분

 

+ 애플리케이션을 설계하고 구축하는 데 사용하는 패턴과 기술

 

애플리케이션 아키텍처에는 다양한 유형이 있지만 서비스 간 관계를 기준으로

  • N-티어 아키텍처(긴밀한 결합)
  • 마이크로서비스(분리됨
  • 이벤트 기반 아키텍처 및 서비스 지향 아키텍처(탄력적 결합)

웹 애플리케이션의 아키텍처는 크게 티어라고 하는 물리층레이어라고 하는 논리층으로 나뉜다.

 

 

 

계층화된 아키텍처는 주로 3개티어(단) 또는 레이어(층)로 구성되지만 이보다 많을 수 있고 

각각 책임이 지정된 티어가 애플리케이션을 구성

프레젠테이션 계층은 웹브라우저를 통해 화면을 보여주는 부분(View)

비즈니스 계층은 실제 로직이 담기는 부분(Model)

데이터베이스 계층은 데이터를 저장하는 부분(Controller)

 

애플리케이션을 개발하는 과정에서 비즈니스 로직과 데이터 로직은 서버에 업데이트함.

프레젠테이션은 클라이언트에서 관리 .

즉 일반적으로 서버는 비즈니스 로직과 데이터 로직을 담고 있으며,

클라이언트는 사용자에게 표현되는 화면을 의미한다.

 

클라이언트와 서버를 분할 관리하면서 생기는 다양한 이점 :

  • 비즈니스로직과 데이터로직의 변경 용이
  • 클라이언트는 업데이트 필요 X - 클라이언트는 사용자의 요청만을 서버에 보내기 때문

dhan-description.tistory.com/67

 

웹 애플리케이션의 이해 (Web Application)

일반적으로 웹 애플리케이션의 아키텍처는 3 계층으로 구성됩니다 1. 프레젠테이션 계층은 웹브라우저를 통해 화면을 보여주는 부분(View)입니다. 2. 비즈니스 계층은 실제 로직이 담기는 부분(Mod

dhan-description.tistory.com

 

반응형
반응형

2021-01-14 수업내용 정리 겸 복습

2021-01-21 추가.

SpringFramework 수업에 들어오면서 벽에 부딪친다라는 느낌을 많이 받았다.

처음에는 잘해보려고 했지만 뭔가 벽에 부딪치는 느낌에 거부감이 계속 들었던 거 같다

수업내용을 정리하다가도 계속 어려움에 처하니 멀리하기 시작했다.

수업내용도 무언가 계속 코딩을 따라가기는 하지만 무언가 구조에 대한 이해라던지

그런 부분에서는 이해를 거의 못하고 있었다. 그래서 계속 다른 길로 새고 그랬던 거 같은데

뭔가 예전의 내 모습 중 어려움을 만나면 일단 피하고 보는 그런 나를 다시금 보는 거 같아서

그러고 싶지 않아서 이해가 안 가도 어려워도 일단 계속 접해보려고 한다.

그래서 다시 내용을 보완한다고 하지만 완벽하진 않을 듯싶다 그래도 계속해보겠다. 


 

스프링(Spring):

스프링은 자바 엔터프라이즈 애플리케이션 개발에 사용되는 프레임워크이다.

스프링은 애플리케이션 프레임워크로 응용프로그램 개발을 빠르고 효율적으로 할 수 있도록

응용프로그램의 바탕이 되는 틀과 공통 프로그래밍 모델, 기술 API 등을 제공합니다. 

 

스프링은 객체지향 언어의 특징을 살려서 개발할 수 있도록 도와주는 도구이며

가장 단순한 객체지향 개발 모델인 POJO프로그래밍을 지향한다.

 

 

 

 

 

스프링 컨테이너 :

Spring Container 또는 Application Context라고 불리는 스프링 런타임 엔진을 제공. 

스프링 컨테이너는 애플리케이션의 기본 틀이며 설정 정보를 참고하여 애플리케이션을 구성하는 객체를 생성하고 관리한다. 스프링 컨테이너는 일반적으로 웹 모듈에서 동작하는 서비스나 서블릿으로 등록하여 사용하고 객체의 생성 소멸과 같은 생명주기 life cycle을 관리한다.

 

-웹 애플리케이션이란 : 

웹 애플리케이션을 여러 사용자가 인터넷을 통해 데이터 베이스에 접근하고 안전하게 정보를 읽고 쓸 수 있게 만들어진, 웹브라우저와 RDB를 이용한 애플리케이션.

 

-JSP, Servlet의 등장 및 Servlet이란 :

멀티스레드로 실행되며, 개발자가 세션 관리를 의식하지 않을 수 있도록 세션을 관리해줌. 기존 CGI 기술로는 페이지 생성 로직과 비즈니스 로직 분리하기 곤란한 반면 JSP로 페이지를 생성하고 비즈니스 로직을 Servlet으로 처리하는 아키텍처도 큰 장점

Servlet이란 자바를 사용하여 웹을 만들기 위해 필요한 기술 좀 더 설명하면 클라이언트가 어떠한 요청을 하면 그에 대한 결과를 다시 전송해주어야 하는데, 이러한 역할을 하는 자바 프로그램

 

-애플리케이션 아키텍처:

애플리케이션 아키텍처는 일반적으로 애플리케이션의 전체의 구조, 공통된 방식(메커니즘)이라 정의 가능

시스템의 애플리케이션이 공통으로 이용할 수 있는 사용자 인터페이스 구조데이터베이스 접근 방식 등 시스템의 기반이 되는 부분

 

-모듈 :

일반적으로 컴퓨터 분야에서의 모듈이라는 용어는, 독립되어 있는 하나의 소프트웨어 또는 하드웨어 단위를 지칭하는 데 사용된다. 

 

 

 

IoC/DI :

객체의 생명주기와 의존 관계에 대한 프로그래밍 모델.

의존성 주입(DI :Dependency Injection)을 통해 각 계층이나 서비스들 간의 의존성을 맞춰줌 

환경설정을 담당하는 XML 파일에 의해 설정되고 수행된다. 

+ 스프링이 제공하는 모든 기술과 API, 컨테이너도 IoC/DI 방식으로 작성되어있다. 

 

 

AOP (Aspect Oriented Programming) :

관점 지향 프로그래밍의 약자로써 스프링의 로깅이나 보안, 트랜잭션 등의 핵심적인 비즈니스 로직과는 관련이 없으나 여러 곳에서 공통적으로 쓰이는 기능들을 분리하여 개발하고 실행 시에 서로 조합할 수 있도록 해준다. 

-로깅 :

시스템 동작 시 시스템 상태/작동 정보를 시간의 경과에 따라 기록하는 것을 로깅, 그 기록을 로그라고 함.

 

-트랜잭션(Transaction) : 

데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위

상태를 변화시킨다는 것은? 간단히 말하면 SQL 질의어를 이용하여 DB에 접근하는 것을 의미

게시판을 예로 들어보자. 

게시판 사용자는 게시글을 작성하고, 올리기 버튼을 누른다. 그 후에 다시 게시판에 돌아왔을 때,

게시판은 자신의 글이 포함된 업데이트된 게시판을 보게 된다.

이러한 상황을 데이터베이스 작업으로 옮기면, 사용자가 올리기 버튼을 눌렀을 시, Insert문을 사용하여

사용자가 입력한 게시글의 데이터를 옮긴다 그 후에, 게시판을 구성할 데이터를 다시 Select 하여 최신 정보로 유지한다

여기서 작업의 단위는 insert문과 select문 둘다를 합친 것 이러한 작업 단위를 하나의 트랜잭션이라 한다.mommoo.tistory.com/62

 

트랜잭션(Transaction)이란?

트랜잭션이란? 트랜잭션(Transaction 이하 트랜잭션)이란, 데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위를 뜻한다. 데이터베이스의 상태를 변화시킨다는 것은 무얼 의미하는 것일

mommoo.tistory.com

 

 

Library vs Framework

라이브러리란 자주 사용하는 코드를 함수나 클래스로 묶어 놓고 가져다 쓰는 것을 의미한다.

 

반면, 프레임워크는 프로그램을 어떻게 짜야한다는 규칙이 있다. 

즉, 프레임워크는 이미 프로그램이 돌아가는 기반 틀이 만들어져 있고 개발자는 그 안에 코드를 작성함으로써 프로그램을 만드는 것 따라서 라이브러리와 달리 누가 하든지 간에 비슷한 코드 품질을 가질 수 있다.

2020/12/24 - [해야 할 일] - 2020-12-23 아몰랑 : API, 프레임워크

 

 

반응형
반응형

2021-01-12 수업

 

JSP프로젝트예제.pdf
1.08MB

예제)

 


내가 작성

 

1. Home

index

<!doctype html>
<html lang="ko">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="./css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
    <title>세미프로젝트v1 내가쓴버전</title>
    <style>
        .fatdiv { padding: 24px; }
        .margin30 { margin: 30px 0; }
        .padding30 {padding :30px 0;}
        .indeximg {height: 300px;}
    </style>
</head>
<body>
<div class="container bg-light">
    <header class="row ">
        <div class="col-5">
        <h2 class=" font-weight-bold"><img src="./img/BrooklynNets/이미지%202021-01-13-41.png" style="width: 50px; height: 50px;">Brooklyn Nets </h2>
        </div>
        <div class="col-7 text-right">
                <h2>
                <button type="button" class="btn btn-danger"
                        data-toggle="modal" data-target="#loginmodal">로그인</button>
                <button type="button" class="btn btn-primary">회원가입</button>
                </h2>
                </div>
    </header>

    <div class="nav navbar-expand navbar-dark bg-dark row">
        <ul class="navbar-nav nav-fill w-100">
            <li class="nav-item"><a class="nav-link" href="index.html">프로젝트 소개</a></li>
            <li class="nav-item"><a class="nav-link" href="#">회원가입</a></li>
            <li class="nav-item"><a class="nav-link" href="#">게시판</a></li>
            <li class="nav-item"><a class="nav-link" href="#">자료실</a></li>
            <li class="nav-item"><a class="nav-link" href="#">갤러리</a></li>
            <li class="nav-item"><a class="nav-link" href="#">관리자</a></li>
        </ul>
    </div>

    <div class="main ">
        <div class="row text-center  padding30">
            <h1 class="col-12 font-weight-bold display-4">BrooklynNets's Best Starting Members !</h1>
        </div>
        <div class="row justify-content-center">
            <img class="col-8  indeximg "  src="./img/BrooklynNets/Brooklyn_Nets.jpg">
        </div>
        <div class="">
            <p class="padding30 font-italic">When the Brooklyn Nets host the Denver Nuggets at Barclays Center on Tuesday night, it will mark three weeks since they opened up the 2020-21 season with a 125-99 win over the Golden State Warriors.

                They’ll go into the game with a 5-6 record, while the Nuggets — Western Conference finalists last season — are 5-5. They’ve got company. Three weeks in, more than half the league is hanging around within a game or two of .500 either way.

                “I think, it’s just a short training camp, so no one really knows who they are, and then you head into a schedule you’re playing more than every other day, it’s hard to resolve and refine your issues,” said Nets head coach Steve Nash. “Not to mention the COVID protocols and whatever things that may go on with your team with other injuries or procedures. I think we’re going to see that for a period of time. I’m sure it will resolve itself at some point in the season, but for now we are I think a team, a league that has a lot of teams that are unsettled. They had short camps. They’re straight into a very heavy schedule and very little practice time, and then all the dynamics of COVID and COVID protocols, which are understandable.”</p>
            <div class="text-center">
            <button type="button" class="btn btn-dark justify-content-center">지금 바로 시작하기 !</button>
            </div>
            </div>

        <div class="row ">
            <div class="col-4">
                <h2 class="font-weight-bold pt-3">NEWS</h2>
                <P class="font-italic">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable.</P>
                <div><button type="button" class="btn btn-dark">자세히 보기&blacktriangleright;</button></div>
            </div>
            <div class="col-4">
                <h2 class="font-weight-bold pt-3">Topic</h2>
                <P class="font-italic">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable.</P>
                <div><button type="button" class="btn btn-dark">자세히 보기&blacktriangleright;</button></div>
            </div>
            <div class="col-4">
                <h2 class="font-weight-bold pt-3">HOT</h2>
                <P class="font-italic">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable.</P>
                <div><button type="button" class="btn btn-dark">자세히 보기&blacktriangleright;</button></div>
            </div>
            <div class="col-4 pb-3">
                <h2 class="font-weight-bold pt-3">THIS</h2>
                <P class="font-italic">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable.</P>
                <div><button type="button" class="btn btn-dark">자세히 보기&blacktriangleright;</button></div>
            </div>
            <div class="col-4 pb-3">
                <h2 class="font-weight-bold pt-3">IS</h2>
                <P class="font-italic">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable.</P>
                <div><button type="button" class="btn btn-dark">자세히 보기&blacktriangleright;</button></div>
            </div>
            <div class="col-4 pb-3">
                <h2 class="font-weight-bold pt-3">NETS</h2>
                <P class="font-italic">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable.</P>
                <div><button type="button" class="btn btn-dark">자세히 보기&blacktriangleright;</button></div>
            </div>
        </div>
    </div>
    <footer class="row">
        <div class="col text-right">
        <h6 class="text-light bg-dark fatdiv">ⓒ Brooklyn Nets 2021 Powered by imlsw96. All Rights Reserved.</h6>
        </div>
    </footer>


</div>



<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="./js/bootstrap.bundle.min.js"></script>
</body>
</html>

테이블 개념?으로 열과 행을  눈으로 박스로 나눠서 코드 작성하니 훨씬 수월했다. 

Bootstrap의 클래스를 이용하니 html, css만으로 작업해야할 부분을 훨씬 수월하게 작업했던 거 같다.  

 

 

2. Modal

 

<!-- 본문에 로그인버튼 수정사항 -->
 <button type="button" class="btn btn-danger"
                        data-toggle="modal" data-target="#loginmodal">로그인</button>
                        



<div id="loginmodal" class="modal">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header bg-dark">
                    <h3 class="text-light font-weight-bold">로그인</h3>
                    <button type="button" class="btn btn-light font-weight-bold" data-dismiss="modal">닫기</button>
            </div><!--헤더 -->
            <div class="modal-body bg-light">
                <form >
                    <div class="form-group row text-center ">
                        <label for="id" class="col-form-label col-4 text-right">아이디</label>
                        <input id="id" type="text" class="form-control col-5">
                    </div>
                    <div class="form-group row text-center ">
                        <label for="pwd" class="col-form-label col-4 text-right">비밀번호</label>
                        <input id="pwd" type="password" class="form-control col-5">
                    </div>

                    <div class="col-group row">
                        <div class="col-4"></div>
                        <div class="form-check">
                            <input type="checkbox" class="form-check-input">
                            <label class="form-check-label">로그인 상태 유지</label>
                        </div>
                    </div>
                </form>
            </div><!-- 바디-->

            <div class="modal-footer justify-content-center bg-dark">
                <button type="button" class="btn btn-danger">로그인</button>
                <button type="button" class="btn btn-warning">아이디/비밀번호 찾기</button>
            </div><!--푸터 -->
        </div><!-- modal-content -->
    </div><!-- modal-dialog -->
</div><!-- modal -->

 

2021-01-13 02:15분 오늘은 여기까지~

 


 

 
반응형

'JAVA &amp; APP :국비지원 학원 복습 > Bootstrap' 카테고리의 다른 글

Bootstrap : 07 Component  (0) 2021.01.12
Bootstrap : 06 Form  (0) 2021.01.11
Bootstrap : 05 table  (0) 2021.01.11
Bootstrap : 04 image, 맥락적인 색/배경  (0) 2021.01.11
Bootstrap : 03 Typography  (0) 2021.01.11
반응형

2020-01-11~12 수업내용 정리 겸 복습

 

스타트 탬플릿

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello, world!</h1>

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>

    <!-- Option 2: jQuery, Popper.js, and Bootstrap JS
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
    -->
  </body>
</html>

Component :

Modal

 

<h1>컴퍼넌트</h1>
<h2>모달대화상자</h2>

<button type="button" class="btn btn-primary" data-toggle="modal", data-target="#target">
모달띄우기</button>

<div class="modal-dialog">
    <div class="modal-content">
        <div class="modal-header">
            <h5 class="modal-title">시간은 금이라구, 친구!</h5>
            <button type="button" class="close" data-dismiss="modal">
                &times;
            </button>
        </div>
        <div class="modal-body">
            <p>times by go on~</p>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-danger" data-dismiss="modal">닫기</button>
            <button type="button" class="btn btn-success">저장하기</button>
        </div>
    </div>
</div>
</div>

 


경고 메시지 : alert

<h2>경고메세지 : alert</h2>
<div class="alert alert-primary" role="alert">
    시간은 금이라구, 친구!
</div>
<div class="alert alert-danger" role="alert">
    시간은 금이라구, 친구!
</div>
<div class="alert alert-dark" role="alert">
    시간은 금이라구, 친구!
</div>


 

배지 : badge

<h2> 뱃지 : badge</h2>
<p>시간은 금이라구, 친구!!
    <span class="badge badge-warning">new</span></p>
<p>시간은 금이라구, 친구!!
    <span class="badge badge-danger">new</span></p>
<p>시간은 금이라구, 친구!!
    <span class="badge badge-pill">new</span></p>


사이트 이동경로 : breadcrumb

 

<h2>사이트 이동경로 : breadcrumb</h2>
<div class="breadcurmb">
<ol class="breadcrumb">
    <li class="breadcrumb-item">home</li>
    <li class="breadcrumb-item">library</li>
    <li class="breadcrumb-item active">data</li>
</ol>
</div>


카드 : card

 

<h2>카드 : card</h2>
<div class="card"  style="width: 18rem">
    <img src="../img/AC_Isabelle.png">
    <div class="card-body">
        <h5>card title</h5>
        <p>가나다라마바사아자차카타파하</p>
        <a href="">card link</a>
        <button type="button">card button</button>
    </div>
</div>


회전하는 이미지 : carousel

<h2>회전하는 이미지 : carousel</h2>
<div id="carouselExampleControls" class="carousel slide"
     data-ride="carousel" style="width: 550px">

    <ol class="carousel-indicators">
        <li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li>
        <li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
        <li data-target="#carouselExampleIndicators" data-slide-to="2"></li>
    </ol>

    <div class="carousel-inner">
        <div class="carousel-item active">
            <img src="../img/Marshal.png" class="d-block">
        </div>
        <div class="carousel-item">
            <img src="../img/llama.png" class="d-block">
        </div>
        <div class="carousel-item">
            <img src="../img/240px-Isabelle_SSBU.png" class="d-block">
        </div>
    </div>

</div>

3개의 그림이 번갈아가며 변경된다.


 

내비게이션 막대 : navbar

 

<h2>네비게이션 막대 : navbar</h2>
<nav class="navbar navbar-expand navbar-light bg-light">
    <a href="#" class="navbar-brand">brand</a>

    <ul class="navbar-nav">
        <li class="nav-item active"><a href="#" class="nav-link">Home</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Docs</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Examples</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Icons</a></li>
        <li class="nav-item"><a href="#" class="nav-link">Themes</a></li>
    </ul>
 </nav>

 


페이지 네비게이션 : pagination

 

<h2>페이지 네비게이션 막대 : pagination</h2>
<ul class="pagination pagination-lg justify-content-center">
    <li class="page-item disabled"><a class="page-link" href="#">◁</a></li>
    <li class="page-item active"><a class="page-link" href="#">2</a></li>
    <li class="page-item"><a class="page-link" href="#">3</a></li>
    <li class="page-item"><a class="page-link" href="#">4</a></li>
    <li class="page-item"><a class="page-link" href="#">5</a></li>
    <li class="page-item"><a class="page-link" href="#">6</a></li>
    <li class="page-item"><a class="page-link" href="#">7</a></li>
    <li class="page-item"><a class="page-link" href="#">8</a></li>
    <li class="page-item"><a class="page-link" href="#">9</a></li>
    <li class="page-item"><a class="page-link" href="#">▷</a></li>
</ul>

반응형
반응형

2021-01-11 수업내용 정리 겸 복습

 

스타트 탬플릿

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello, world!</h1>

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>

    <!-- Option 2: jQuery, Popper.js, and Bootstrap JS
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
    -->
  </body>
</html>

간단한 폼: form-group, form-control을 반드시 쓰자

<h2>간단한 폼 : form-group, form-control을 반드시 쓰자 </h2>
<form>
    <!-- 반드시 폼그룹을 만들어 주어야 폼 사용 가능 -->
    <div class="form-group">
        <label for="email">이메일주소</label>
        <input type="text" id="email" class="form-control">
        <small class="text-muted form-text">이메일 주소는 올바르게 쓰세요.</small>
    </div>
    <div class="form-group">
        <label for="pwd">비밀번호</label>
        <input type="password" id="pwd" class="form-control">
        <small class="text-muted form-text">비밀번호 똑디 써라~</small>
    </div>
    <div class="form-group form-check">
        <input type="checkbox" id="check1" class="form-check-input">
        <label class="form-check-label">자동로그인</label>
    </div>
    <button type="button" class="btn btn-primary">로그인</button>
</form>


수평 배치 폼: form-group row

 

<h2>수평배치 폼 : form-group row</h2>
<form>
    <div class="form-group row">
        <label for="userid" class="col-form-labe col-sm-2">아이디</label>
        <input type="text" id="userid" class="form-control col-sm-5 ">
    </div>
    <div class="form-group row">
        <label for="userpwd" class="col-form-label col-sm-2">비밀번호</label>
        <input tpye="password" id="userpwd" class="form-control col-sm-5">
    </div>
</form>


인라인 폼 : form-inline

<h2>인라인 폼 : form-inline</h2>
<form class="form-inline">
    <div class="form-group col-5">
    <label for="userid2" class="col-form-label ">아이디</label>
    <input tpye="text" id="userid2" class="form-control">
    </div>
    <div class="form-group col-5">
        <label for="pwd2" class="col-form-label ">비밀번호</label>
        <input tpye="password" id="pwd2" class="form-control">
    </div>
</form>


체크박스 수평 배치 : form-check

<h2>체크박스 수평배치 : form-check </h2>
<form class="form-group m-1">
    <div class="form-check">
        <input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1">
        <label class="form-check-label" for="inlineCheckbox1">1</label>
    </div>
    <div class="form-check ">
        <input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2">
        <label class="form-check-label" for="inlineCheckbox2">2</label>
    </div>
    <div class="form-check">
        <input class="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3" disabled>
        <label class="form-check-label" for="inlineCheckbox3">3 (disabled)</label>
    </div>
</form>

라디오 버튼 인라인 배치 : form-check-inline

<h2>라디오버튼 인라인 배치 form-check-inline : </h2>
<form>
    <div class="form-check form-check-inline">
        <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
        <label class="form-check-label" for="inlineRadio1">1</label>
    </div>
    <div class="form-check form-check-inline">
        <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
        <label class="form-check-label" for="inlineRadio2">2</label>
    </div>
    <div class="form-check form-check-inline">
        <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3" disabled>
        <label class="form-check-label" for="inlineRadio3">3 (disabled)</label>
    </div>
</form>

 

반응형
반응형

2021-01-11 수업내용 정리 겸 복습

 

기본 스타터 탬플릿

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello, world!</h1>

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>

    <!-- Option 2: jQuery, Popper.js, and Bootstrap JS
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
    -->
  </body>
</html>

 


기본 테이블

<table class="table table-dark">
    <thead>
    <tr><th scope="col">이름</th>
        <th scope="col">국어</th>
        <th scope="col">영어</th>
        <th scope="col">수학</th></tr></thead>
    <tbody>
    <tr><td>혜교</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>제니</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>수지</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>지현</td><td>98</td><td>88</td><td>100</td></tr>
    </tbody>
</table>


다양한 테이블 옵션 : thead-dark, thead-light

<h2>다양한 테이블 옵션 : thead-dark, thead-light</h2>
<table class="table">
    <thead class="thead-light">
    <tr><th scope="col">이름</th>
        <th scope="col">국어</th>
        <th scope="col">영어</th>
        <th scope="col">수학</th></tr></thead>
    <tbody>
    <tr><td>혜교</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>제니</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>수지</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>지현</td><td>98</td><td>88</td><td>100</td></tr>
    </tbody>
</table>

+ 얼룩말 무늬 테이블 : table-striped

<h2>얼룩말 무늬 테이블 : table-striped</h2>
<table class="table table-striped">
    <thead>
    <tr><th scope="col">이름</th>
        <th scope="col">국어</th>
        <th scope="col">영어</th>
        <th scope="col">수학</th></tr></thead>
    <tbody>
    <tr><td>혜교</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>제니</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>수지</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>지현</td><td>98</td><td>88</td><td>100</td></tr>
    </tbody>
</table>


테두리가 있는 테이블 : table-borderd

<h2>테두리가 있는 테이블 : table-borderd</h2>
<table class="table table-bordered">
    <thead>
    <tr><th scope="col">이름</th>
        <th scope="col">국어</th>
        <th scope="col">영어</th>
        <th scope="col">수학</th></tr></thead>
    <tbody>
    <tr><td>혜교</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>제니</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>수지</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>지현</td><td>98</td><td>88</td><td>100</td></tr>
    </tbody>
</table>


선택행을 보여주는 테이블 : table-hover

<h2>선택행을 보여주는 테이블 : table-hover</h2>
<table class="table table-hover">
    <thead>
    <tr><th scope="col">이름</th>
        <th scope="col">국어</th>
        <th scope="col">영어</th>
        <th scope="col">수학</th></tr></thead>
    <tbody>
    <tr><td>혜교</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>제니</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>수지</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>지현</td><td>98</td><td>88</td><td>100</td></tr>
    </tbody>
</table>

마우스로 갖다대는 열에 색이 생긴다.


맥락이 있는 행을 보여주는 테이블

<h2>맥락이 있는 행을 보여주는 테이블 r</h2>
<table class="table table-hover">
    <thead>
    <tr><th scope="col">이름</th>
        <th scope="col">국어</th>
        <th scope="col">영어</th>
        <th scope="col">수학</th></tr></thead>
    <tbody>
    <tr class="bg-warning"><td>혜교</td><td>98</td><td class="bg-danger">88</td><td>100</td></tr>
    <tr><td>제니</td><td>98</td><td>88</td><td>100</td></tr>
    <tr><td>수지</td><td class="bg-success">98</td><td>88</td><td>100</td></tr>
    <tr><td>지현</td><td>98</td><td>88</td><td>100</td></tr>
    </tbody>
</table>

반응형

'JAVA &amp; APP :국비지원 학원 복습 > Bootstrap' 카테고리의 다른 글

Bootstrap : 07 Component  (0) 2021.01.12
Bootstrap : 06 Form  (0) 2021.01.11
Bootstrap : 04 image, 맥락적인 색/배경  (0) 2021.01.11
Bootstrap : 03 Typography  (0) 2021.01.11
Bootstrap : 02 Layout  (0) 2021.01.11
반응형

2021-01-11 수업내용 정리 겸 복습

 

기본 스타터 탬플릿

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello, world!</h1>

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>

    <!-- Option 2: jQuery, Popper.js, and Bootstrap JS
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
    -->
  </body>
</html>

반응형 이미지

<h2>반응형 이미지 : img-responsive</h2>
<img src="../img/free-icon-line-124027.png" class="img-fluid" alt="반응형 이미지">

창이 줄어듬에 따라 사진도 작아지는걸 확인할 수 있다.

이미지 썸네일

<h2>이미지 썸네일</h2>
<img src="../img/free-icon-fried-chicken-2674070.png" class="img-thumbnail ">

 

회색테두리가 생기고 모서리가 둥글어지는 효과가 생긴다.


맥락적인 색/배경

<h2>맥락적인 색 : </h2>
<p class="text-muted">가나다라마바사아자차카타파하</p>
<p class="text-primary">가나다라마바사아자차카타파하</p>
<p class="text-success">가나다라마바사아자차카타파하</p>
<p class="text-info">가나다라마바사아자차카타파하</p>
<p class="text-warning">가나다라마바사아자차카타파하</p>
<p class="text-danger">가나다라마바사아자차카타파하</p>

<h2>맥락적인 배경 : </h2>
<p class="bg-muted">가나다라마바사아자차카타파하</p>
<p class="bg-primary">가나다라마바사아자차카타파하</p>
<p class="bg-success">가나다라마바사아자차카타파하</p>
<p class="bg-info">가나다라마바사아자차카타파하</p>
<p class="bg-warning">가나다라마바사아자차카타파하</p>
<p class="bg-danger">가나다라마바사아자차카타파하</p>

반응형

'JAVA &amp; APP :국비지원 학원 복습 > Bootstrap' 카테고리의 다른 글

Bootstrap : 06 Form  (0) 2021.01.11
Bootstrap : 05 table  (0) 2021.01.11
Bootstrap : 03 Typography  (0) 2021.01.11
Bootstrap : 02 Layout  (0) 2021.01.11
Bootstrap : 01 Hello  (0) 2021.01.11

+ Recent posts