create user 'spring'@'%' identified by 'bitc5600';
GRANT ALL PRIVILEGES ON *.* TO 'spring'@'%';
create database spring;
use spring;

CREATE TABLE user(
	id int auto_increment primary key,
    username varchar(100) unique not null,
    password varchar(100) not null,
    email varchar(100),
    profile varchar(200),
    createDate timestamp
) engine=InnoDB default charset=utf8;

CREATE TABLE post(
	id int auto_increment primary key,
    title varchar(100) not null,
    content longtext,
    userId int,
    createDate timestamp,
    foreign key (userId) references user (id) on delete set null
) engine=InnoDB default charset=utf8;

CREATE TABLE comment(
	id int auto_increment primary key,
    userId int,
    postId int,
    content varchar(300) not null,
    createDate timestamp,
    foreign key (userId) references user (id) on delete set null,
    foreign key (postId) references post (id) on delete cascade
) engine=InnoDB default charset=utf8;

 

 

 

 

 

글목록 보기

IndexController

 

 

PostController

 

 

PostService

 

 

PostRepository

 

 

post_mapper

 

 

jstl 코어 추가

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

 

index.jsp

 

--------------

mysql 한글설정

https://blog.naver.com/getinthere/221765250539

 

JSP 게시판 만들기 2강 - MySQL 한글 세팅하기

윈도우와 리눅스가 달라요!!1. 윈도우(1) my.ini 파일 찾기(2) 관리자 권한으로 my.ini 파일 열어서 수정하...

blog.naver.com

 

https://ondolroom.tistory.com/496

 

mysql 세팅

https://getinthere.tistory.com/17 스프링부트 with JPA 블로그 3강 - MySQL 한글 설정 및 연결 1. MySQL 한글 설정 my.ini 파일 MySQL 재시작 [client] default-character-set=utf8 [mysql] default-character-s..

ondolroom.tistory.com

---------------

 

링크달기

 

 

-------------

안드로이드 리액트

@GetMapping("/v1/post/{id}")

이런방식으로...

--------------

 

PostController

 

PostService

 

PostRepository

 

PostDetailRespDto

 

post_mapper

 

 

 

 

 

 

 

딜리트 끝나면 메인페이지로

 

삭제하기

 

detail.jsp

 

 

post.jsp

더보기
let index = { // 여기서는 이벤트 리스너를 바인딩만 하고
	init : function() {
		
		$("#btn-save").on("click", ()=> { // 리스너
			this.save(); // 콜백
		});
		
		$("#btn-delete").on("click", ()=> { // 리스너
			this.deleteById(); // 콜백
		});
		
	},
	
	save: function() { // 실제 로직은 여기서 실행
		
		let data ={
				title : $("#title").val(),
				content : $("#content").val(),
				userId : $("#userId").val()
		};
		
		$.ajax({ // 주소는 RESTAPI 방식대로 적어야한다 
			
			type:"post",
			url: "/post",
			data: JSON.stringify(data),
			contentType: "application/json; charset=utf-8",  // http에서는
																// Content-Type
																// 라고 씀
			dataType: "json"  // 스프링은 응답할때 스트링일 경우 무조건 json으로 응답한다
			
		}).done((resp)=>{
			
			alert("글쓰기 성공");
			console.log(resp);
			location.href="/";
			
		}).fail((error)=>{
			
			alert("글쓰기 실패");
			console.log(error);
			
		});
		
	},
	
	deleteById: function() { // 실제 로직은 여기서 실행
		
		let data ={
				id : $("#id").val(),
		};
		
		$.ajax({ // 주소는 RESTAPI 방식대로 적어야한다 
			
			type:"delete",
			url: "/post/"+data.id,
//			data: JSON.stringify(data),
//			contentType: "application/json; charset=utf-8",
			dataType: "json"  // 스프링은 응답할때 스트링일 경우 무조건 json으로 응답한다
			
		}).done((resp)=>{
			
			alert("삭제 성공");
			location.href="/";
			
		}).fail((error)=>{
			
			alert("삭제 실패");
			console.log(error);
			
		});
		
	}
}

index.init();

 

 

 

 

PostController

 

 

PostService

 

 

PostRepository

 

 

post_mapper

 

-----------

 

수정버튼 누르면 수정모드로 변경

 

post.js

 

 

 

 

----------------

 

게시물 수정하기

 

post.js

 

 

 

PostController

 

 

PostService

 

 

PostRepository

 

 

post_mapper

 

 

-------------------

해당 태그 숨기기 보여주기

$(선택자).hide();
$(선택자).show();

-----------------

 

삭제시 본인 확인

 

post.js

 

PostController

방식1

 

 

 

 

 

 

RestController

 

 

 

역할 추가

 

CREATE TABLE user(
	id int auto_increment primary key,
    username varchar(100) unique not null,
    password varchar(100) not null,
    email varchar(100),
    profile varchar(200),
    role VARCHAR(50),
    createDate timestamp
) engine=InnoDB default charset=utf8;

CREATE TABLE post(
	id int auto_increment primary key,
    title varchar(100) not null,
    content longtext,
    userId int,
    createDate timestamp,
    foreign key (userId) references user (id) on delete set null
) engine=InnoDB default charset=utf8;

CREATE TABLE comment(
	id int auto_increment primary key,
    userId int,
    postId int,
    content varchar(300) not null,
    createDate timestamp,
    foreign key (userId) references user (id) on delete set null,
    foreign key (postId) references post (id) on delete cascade
) engine=InnoDB default charset=utf8;

 

 

 

매퍼

 

 

 

회원가입하고 DB에서 root 어드민으로 변경

(유저롤을 만들 때에는 ROLE_ 을 붙여주는 것이 좋다)

 

 

조건문 없이 삭제 갱신 등을 하면 막는 옵션 체크 해제 

 

 

오토커밋해제 (롤백을 위한)

 

 

---

 

인터셉터

 

 

 

 

 

 

 

 

 

(Dispatcher Servlet에서 스프링으로 보낼때 사이에서 핸들러가 중간에 낄 수 있다.)

핸들러 - 주소매핑

 

 

 

 

 

 

---

디폴트로 만들어진 인터페이스는 구체화하지 않아도 오류가 나지 않는다

---

 

 

 

 

내용변경

 

 

 

 

자동으로 인터셉트한다.

 

추후 webMvcConfigurer 가 다른것으로 바뀐다면 인터셉터에 접근하는 방법을 찾아야한다
(WebFlux 등)

인터셉터 + 필터 = 시큐리티

 

AOP =관점 지향 프로그래밍 - 함수의 직전과 직후를 처리할 수 있다

회원가입
직전 - 유효성체크
본문 - 회원가입
직후 - 세션등록 및 로그인처리

직전과 직후의 부가적인 기능을 밖으로 빼고 본문의 기능(관심 로직)을 작성

부가적인 기능은 [공통적]으로 쓸 수 있게 작성해놓고 사용 !! -> 핵심

 

 

 

 

 

로그인후 이용하면 접속된다

 

 

----------------

Log4J Logger

-------------

 

 

 

 

로그인을 해도 어드민이 아니라면 익셉션

 

 

어드민으로 로그인해서 접속하면

관리자페이지로 접속

 

 

 

 

(이렇게 익셉션별로 구분해야하나 지금은 Exception으로 통일)

 

 

 

 

 

 

 

 

 

 

 

 

IO익셉션으로 설정해놓으면 터진다

 

----------

로그기록 api

https://sentry.io/welcome/

 

Application Monitoring and Error Tracking Software

Self-hosted and cloud-based error monitoring that helps software teams discover, triage, and prioritize errors in real-time.

sentry.io

-----------

 

https://blog.naver.com/getinthere/221718319587

 

springboot 8강 - Spring 인터셉터

인터셉터란 PPT아래와 같이 두 가지만 파일만 정의해주면 된다.config/WebMvcConfig.javaconfig/Ses...

blog.naver.com

 

핸들러는 인터셉터다

 

Spring+HandlerInterceptor.pptx
0.64MB

 

 

 

 

---

컨트롤러 - 뷰리졸버 발동 (@ResponseBody쓰면 미발동)

Rest컨트롤러 - 뷰리졸버 미발동

------

 

Dispatcher Servlet 에서 FrontController 타고 Intercepter가 있으면 탄다

 

 

 

 

 

 

modelAndView 는 JSP에서의 모델이다
response 시 같이 가져가게된다

 

 

프리핸들러 세션 등을 확인

 

 

---

레거시 사용시

web.xml ------- 톰캣 시작시 발동

root-context.xml  -------- 메모리에 딱 1번만 올릴 것들 (DB관련 등) // 톰캣 시작시 발동
servlet-context.xml -------- 요청 올 때마다 메모리에 띄울 것들 // Request할 때 발동된다

----------

aop는 요청시마다 생겨야하니까 servlet-context.xml

----------

 

로그아웃

------------

 

redirect: 를 사용하면 파일이 아닌 주소를 검색한다

 

-----------

 

글쓰기

 

세션 인터셉터에 등록

 

 

 

---------------------

 

 

-------


REST API 주소 설계 방법

주소를 여러개 생성하지 않고도 REST 방식으로 처리 가능

데이터베이스에 접근하는 모든 주소!

SELECT 1건 ------> /post/{id}         (GET)
SELECT ALL ------> /posts             (GET)

INSERT ------> /post                    (POST)

UPDATE ------> /post                   (PUT)

DELETE 1건 ------> /post/{숫자}      (DELETE)
DELETE ALL ------> /posts             (DELETE)

-------

1번 유저가 들고 있는 2번 상품을 1건 확인 ->  /user/1/product/2 (GET)

1번 유저가 들고 있는 2번 상품을 1건 업데이트 ->  /user/1/product/2 (PUT)

1번 유저가 들고 있는 2번 상품을 1건 삭제 ->  /user/1/product/2 (DELETE)

1번 상품을 들고 있는 모든 유저 ->  /product/1/users (GET)

------

쿼리스트링보다 RESTful을 사용하자

---------

autowired 방식

3가지

 

1

 

2

 

3

 

4

 

----------------

 

let index = { // 여기서는 이벤트 리스너를 바인딩만 하고
	init : function() {
		
		$("#btn-save").on("click", ()=> { // 리스너
			this.save(); // 콜백
		});
		
	},
	
	save: function() { // 실제 로직은 여기서 실행
		
		let data ={
				title : $("#title").val(),
				content : $("#content").val(),
				userId : $("#userId").val()
		};
		
		$.ajax({ // 주소는 RESTAPI 방식대로 적어야한다 
			
			type:"post",
			url: "/post",
			data: JSON.stringify(data),
			contentType: "application/json; charset=utf-8",  // http에서는
																// Content-Type
																// 라고 씀
			dataType: "json"  // 스프링은 응답할때 스트링일 경우 무조건 json으로 응답한다
			
		}).done((resp)=>{
			
			alert("글쓰기 성공");
			console.log(resp);
			location.href="/";
			
		}).fail((error)=>{
			
			alert("글쓰기 실패");
			console.log(error);
			
		});
		
	}
}

index.init();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

스타일 컴포넌트는 무조건 클래스 밖으로 빼는 것을 권장한다

 

추가 concat
삭제 filter
검색 filter
반복 map
수정 map
중간요소 추가 slice + concat

스프레드 연산자 [...]

 

중간요소추가

 

1,2,3을  1,2,4,3으로 만드는 법

 

비효율적인 방법

 

 

 

짧게 하는 법!

 

 

----

스프레드 연산자

장점
1. 깊은 복사에 좋다
2. 오브젝트 결합시 같은값을 중복처리하여 바꾼다

 

 

 

----------

 

 

 

-----------

 

 

 

 

스프레드 연산자로 a 와 b를 결합 (배열일 경우!!!)

 

 

-------

 

얕은 복사

 

깊은 복사

 

----

 

 

스프레드 연산자로 오브젝트 연산시 다른 현상이 일어난다(중요)

 

데이터가 덮어씌워진다

 

 

완전히 덮어씌어진다

 

 

 

d 라는 중복이 제거된다

 

 

맨위의 중간에 추가하는 기능을 스프레드로

 

 

 

----------------

 

검색

 

 

 

 

 

 

 

 

 

---------------

수정

 

 

 

 

 

 

 

이렇게하면 모양이 망가진다

 

 

 

 

 

 

 

오브젝트 결합시 덮어씌워지고 합쳐진다.(중요)

 

----------

 

immer (불변성 관리도구 / 스프레드관련)

 

---------

 

버츄얼돔
- state의 내용 변화를 연산한다 (내용이 일치하는지 달라졌는지 전체 확인) -> 연산후 변화된 부분 렌더링
- 연산이 많아지면 느려진다
- ShouldComponentUpdate -> 연산할지 말지 결정
if(prvState.num === State.num){return false true} -- >
같은배열이면 연산X, 다른배열이면 연산O , 깊은 복사로 연산을 줄일 수 있음(불변성)

 

-----------

 

수정하기

 

더보기
import React, { Component } from "react";
import Post from "./Post";
import styled from "styled-components";

const ContainerBox = styled.div`
  display: grid;
  justify-content: center;
`;

class App extends Component {
  state = {
    posts: [
      {
        id: 1,
        title: "제목1",
      },
      {
        id: 2,
        title: "제목2",
      },
      {
        id: 3,
        title: "제목3",
      },
    ],
  };

  render() {
    const add = () => {
      // render안에 const로 만들면 계속 초기화 되지않는다
      this.setState({
        posts: this.state.posts.concat({
          id: 4,
          title: "제목4",
        }),
      });
    };

    const del = () => {
      // render안에 const로 만들면 계속 초기화 되지않는다
      this.setState({
        posts: this.state.posts.filter((post) => {
          return post.id !== 2;
        }),
      });
    };

    const update = () => {
      const data = {
        id: 2,
        title: "제목200",
      };

      this.setState({
        posts: this.state.posts.map((post) =>
          post.id === 2 ? { ...post, ...data } : post
        ),
      });
    };

    return (
      <ContainerBox>
        <button onClick={add}> 추가 </button>
        <button onClick={del}> 삭제 </button>
        <button onClick={update}> 수정 </button>
        {this.state.posts.map((post) => {
          return <Post id={post.id} title={post.title} />;
        })}{" "}
      </ContainerBox>
    );
  }
}

export default App;

 

 

내용이 변경된다

 

-------------

 

 

 

 

 

 

 

 

 

코드 확인

https://github.com/jaybon1/androidwork/tree/master/instagramRecycleView

 

jaybon1/androidwork

Contribute to jaybon1/androidwork development by creating an account on GitHub.

github.com

 

영화 포스터 앱

item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp">

    <ImageView
        android:layout_width="60dp"
        android:layout_height="wrap_content"
        android:src="@drawable/mov01"
        android:layout_alignParentLeft="true"
        android:scaleType="centerCrop"/>

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="99dp"
        android:text="의미없음"
        android:textSize="30sp"
        android:textStyle="bold"
        android:gravity="center"/>

    <View
        android:layout_below="@id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@android:color/darker_gray"/>

</RelativeLayout>

 

 

alt + insert

 

package com.jaybon.movie;

import android.media.Image;
import android.text.Layout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import org.w3c.dom.Text;

import java.util.ArrayList;
import java.util.List;

public class SingleAdapter extends BaseAdapter {

    private static final String TAG = "SingleAdapter";

    // 데이터
    private List<Movie> items = new ArrayList<>();

    public SingleAdapter() {
        items.add(new Movie("써니",R.drawable.mov01));
        items.add(new Movie("완득이",R.drawable.mov02));
        items.add(new Movie("괴물",R.drawable.mov03));
        items.add(new Movie("라디오스타",R.drawable.mov04));
        items.add(new Movie("비열한거리",R.drawable.mov05));
        items.add(new Movie("왕의남자",R.drawable.mov06));
        items.add(new Movie("아일랜드",R.drawable.mov07));
        items.add(new Movie("웰컴투동막골",R.drawable.mov08));
        items.add(new Movie("헬보이",R.drawable.mov09));
        items.add(new Movie("백투더퓨처",R.drawable.mov10));
        items.add(new Movie("여인의향기",R.drawable.mov11));
        items.add(new Movie("쥬라기공원",R.drawable.mov12));
    }

    // 모든건수 받기
    public void addItems(List<Movie> items){
        this.items = items;
    }

    // 한건씩 받기 생략
    
    // getCount과 getItem는 필수!

    @Override
    public int getCount() { // 데이터 사이즈, 최초에 화면을 몇건 만들 것인지
        Log.d(TAG, "getCount: ");
        return items.size();
    }

    @Override
    public Object getItem(int position) { // 아이템 가져오기
        Log.d(TAG, "getItem: ");
        return items.get(position);
    }

    @Override
    public long getItemId(int position) {
        Log.d(TAG, "getItemId: ");
        return 0;
    }


    // 집중!
    // 메모리에 인플레이트해서 화면에 뿌려주는 것
    @Override
    public View getView(int position, View convertView, ViewGroup parent) { // 포지션값은 제일 밑에 값
        Log.d(TAG, "getView: " +position);

        // 레이아웃 인플레이터로 인플레이터 객체 접근하기
        LayoutInflater inflater = LayoutInflater.from(parent.getContext()); // 인플레이터

        // 메모리에 아이템 하나를 인플레이터하기
        View itemView = inflater.inflate(R.layout.item, parent, false); //아이템뷰

        // View 찾기
        TextView tv = itemView.findViewById(R.id.tv_title); //뷰에서 텍스트뷰 찾기
        ImageView iv = itemView.findViewById(R.id.iv_img_resource); //뷰에서 이미지뷰 찾기

        String title = ((Movie) getItem(position)).getTitle();
        int imageResource = ((Movie) getItem(position)).getImgResource();

        tv.setText(title);
        iv.setImageResource(imageResource);

        return itemView;
    }
}

 

 

--------------------------

어댑터에 5건이 있다면

리스트뷰 - 화면을 이동할 때마다 인플레이터가 계속 많이 실행된다 (뷰가 계속 생성됨) - 세로만가능

리사이클러뷰 - 만들어진 틀에다가 데이터만 교체(뷰는 유지 데이터변경), 뷰홀더 패턴 - 가로세로가능

 

--------------------------

 

리사이클러 뷰

 

https://codinginflow.com/

 

Tutorials - Coding in Flow

⭐ Newest Tutorials 🔠 Language Tutorials Kotlin Android Tutorials 🏛️ Architecture & Best Practices Room + ViewModel + LiveData + RecyclerView (MVVM) Dagger 2 View Binding Data Binding Testing 📏 ... Read more

codinginflow.com

 

 

 

다운로드

 

 

MovieAdapter

 

 

package com.jaybon.recyclerviewmovieex01;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MyViewHolder> {

    private static final String TAG = "MovieAdapter";
    
    // 어댑터는 컬렉션을 들고 있어야한다
    private List<Movie> items = new ArrayList<>();


    // 리스트뷰의 getView와 비슷하다
    // 껍데기만! 만들어줌! 1번으로 실행됨
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.item,parent,false);
        return new MyViewHolder(view);
    }

    // 껍데기에 데이터 바인딩 해줌. 2번으로 실행됨
    // holder는 뷰홀더의 주소를 가지고 있다
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        // 아이템의 포지션값을 이용해서 데이터를 가져옴
        Movie movie = items.get(position);
        // 홀더에 아이템넣기
        holder.setItem(movie);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    // ViewHolder (뷰들의 책꽂이)
    public static class MyViewHolder extends RecyclerView.ViewHolder { // 뷰홀더

        // 규칙1 (xml이 들고있는 뷰)
        private TextView tvTitle;
        private ImageView ivImgResource;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvTitle = itemView.findViewById(R.id.tv_title);
            ivImgResource = itemView.findViewById(R.id.iv_img_resource);
        }

        // 규칙3
        public void setItem(Movie movie){
            tvTitle.setText(movie.getTitle());
            ivImgResource.setImageResource(movie.getImgResource());
        }

    }

}

 

MainActivity

package com.jaybon.recyclerviewmovieex01;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";
    private RecyclerView rvMovie;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rvMovie = findViewById(R.id.rv_movie);
        MovieAdapter adapter = new MovieAdapter();

        // 외부에서 데이터 받을때 쓰레드로 받아주면 됨
        adapter.addItem(new Movie("써니",R.drawable.mov01));
        adapter.addItem(new Movie("완득이",R.drawable.mov02));
        adapter.addItem(new Movie("괴물",R.drawable.mov03));
        adapter.addItem(new Movie("라디오스타",R.drawable.mov04));
        adapter.addItem(new Movie("비열한거리",R.drawable.mov05));
        adapter.addItem(new Movie("왕의남자",R.drawable.mov06));
        adapter.addItem(new Movie("아일랜드",R.drawable.mov07));
        adapter.addItem(new Movie("웰컴투동막골",R.drawable.mov08));
        adapter.addItem(new Movie("헬보이",R.drawable.mov09));
        adapter.addItem(new Movie("백투더퓨처",R.drawable.mov10));
        adapter.addItem(new Movie("여인의향기",R.drawable.mov11));
        adapter.addItem(new Movie("쥬라기공원",R.drawable.mov12));

        // 레이아웃매니저 설정(버티컬인지 호라이즌인지)
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
        rvMovie.setLayoutManager(layoutManager);
        rvMovie.setAdapter(adapter);
    }
}

 

 

----------------

---------------

 

인스타그램 UI에 리사이클러 뷰 넣기

 

 

 

 

 

리사이클러 뷰 아이템은 merge를 사용하지말것

 

https://github.com/jaybon1/androidwork/tree/master/instagramRecycleView

 

jaybon1/androidwork

Contribute to jaybon1/androidwork development by creating an account on GitHub.

github.com

 

 

--------------

 

 

 

 

 

 

 

styled component 설치

 

 

state의 데이터를 map을 이용 해서 입력

 

Post.js

import React from "react";
import styled from "styled-components";

const PostBox = styled.div`
  height: 100px;
  width: 300px;
  border: 1px solid black;
`;

const Post = (props) => {
  return <PostBox>{props.title}</PostBox>;
};

export default Post;

 

App.js

import React, { Component } from "react";
import Post from "./Post";
import styled from "styled-components";

class App extends Component {
  state = {
    posts: [
      {
        id: 1,
        title: "제목1",
      },
      {
        id: 2,
        title: "제목2",
      },
      {
        id: 3,
        title: "제목3",
      },
    ],
  };

  render() {
    const ContainerBox = styled.div`
      display: grid;
      justify-content: center;
    `;

    return (
      <ContainerBox>
        {this.state.posts.map((post) => {
          return <Post id={post.id} title={post.title} />;
        })}
      </ContainerBox>
    );
  }
}

export default App;

 

 

 

 

 

추가를 누르면 리스트가 아이템이 추가되도록 할 것

 

자바스크립트 배열 추가할 때는 무조건 concat 쓰자

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let a = [{
            id: 1,
            username: 'ssar'
        }, {
            id: 1,
            username: 'ssar'
        }];
        console.log(a);
        let b = [{
            id: 2,
            username: 'cos'
        }];
        a = a.concat(); // 변수, 객체, 배열 다 들어갈 수 있다.
        console.log(a);
    </script>
</body>

</html>

 

 

App.js

import React, { Component } from "react";
import Post from "./Post";
import styled from "styled-components";

class App extends Component {
  state = {
    posts: [
      {
        id: 1,
        title: "제목1",
      },
      {
        id: 2,
        title: "제목2",
      },
      {
        id: 3,
        title: "제목3",
      },
    ],
  };

  render() {
    const ContainerBox = styled.div`
      display: grid;
      justify-content: center;
    `;

    const add = () => {
      // render안에 const로 만들면 계속 초기화 되지않는다
      this.setState({
        posts: this.state.posts.concat({
          id: 4,
          title: "제목4",
        }),
      });
    };

    return (
      <ContainerBox>
        <button onClick={add}>추가</button>

        {this.state.posts.map((post) => {
          return <Post id={post.id} title={post.title} />;
        })}
      </ContainerBox>
    );
  }
}

export default App;

 

------------------

 

배열 필터링

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let a = [1, 2, 3, 4];
        a = a.filter((n) => n == 3);
        console.log(a);
    </script>
</body>

</html>

 

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let a = [{
            id: 1
        }, {
            id: 2
        }, {
            id: 3
        }, {
            id: 4
        }];
        let b = a.filter((n) => n.id != 3);
        console.log(b);
    </script>
</body>

</html>

 

 

--------------------

 

App.js

import React, { Component } from "react";
import Post from "./Post";
import styled from "styled-components";

class App extends Component {
  state = {
    posts: [
      {
        id: 1,
        title: "제목1",
      },
      {
        id: 2,
        title: "제목2",
      },
      {
        id: 3,
        title: "제목3",
      },
    ],
  };

  render() {
    const ContainerBox = styled.div`
      display: grid;
      justify-content: center;
    `;

    const add = () => {
      // render안에 const로 만들면 계속 초기화 되지않는다
      this.setState({
        posts: this.state.posts.concat({
          id: 4,
          title: "제목4",
        }),
      });
    };

    const del = () => {
      // render안에 const로 만들면 계속 초기화 되지않는다
      this.setState({
        posts: this.state.posts.filter((post) => {
          return post.id !== 2;
        }),
      });
    };

    return (
      <ContainerBox>
        <button onClick={add}>추가</button>
        <button onClick={del}>삭제</button>

        {this.state.posts.map((post) => {
          return <Post id={post.id} title={post.title} />;
        })}
      </ContainerBox>
    );
  }
}

export default App;

 

 

 

 

 

 

 

MainActivity

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


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="60sp"
            android:text="메인 액티비티"/>

    </LinearLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav1"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        app:menu="@menu/nav_menu"
        android:layout_gravity="start|left"/>

</androidx.drawerlayout.widget.DrawerLayout>

무조건! 리니어 레이아웃 아래에 배치

레이아웃 위치에 따라 작동 여부가 되는 경우가 있고 안되는 경우가 있다

 

 

---------------

headerLayout = 유저 정보

---------------

 

 

 

 

 

 

SubActivity

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SubActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="60sp"
            android:text="서브 액티비티"/>

    </LinearLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        app:menu="@menu/nav_menu"
        android:layout_gravity="start|left"/>

</androidx.drawerlayout.widget.DrawerLayout>

 

 

https://lktprogrammer.tistory.com/168

 

[Android] 안드로이드 - 네비게이션 드로어(Navigation Drawer)를 활용하여 슬라이드 메뉴 구현하기

내비게이션 드로어(Navigation Drawer)는 앱에서 사용 가능한 기능을 탐색할 수 있도록 Menu를 제공하는 화면입니다. 기본적으로 화면의 가장자리에 숨겨져 있으며 왼쪽에서 오른쪽으로 스와이프 동��

lktprogrammer.tistory.com

 

 

package com.jaybon.navigationintentex01;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;

import com.google.android.material.navigation.NavigationView;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";

    private NavigationView nav;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        nav = findViewById(R.id.nav1);

        nav.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId())
                {
                    case R.id.nav_menu1:
                        Log.d(TAG, "onNavigationItemSelected: 확인1");
                        break;
                    case R.id.nav_menu2:
                        Log.d(TAG, "onNavigationItemSelected: 확인2");
                        break;
                }
                return false;
            }
        });



    }
}

 

 

 

(이렇게 리스너 달지 않는다 ! 연습용이었다고함)

 

 

 

--------

SQL라이트 - ORM - ROOM

---------

MVVM - AAC - SQL라이트 패틴

--------

FCM

-------

 

헬퍼 클래스를 만들어서 리스너 추가

 

NavigationViewHelper

package com.jaybon.navigationintentex01;

import android.content.Context;
import android.content.Intent;
import android.view.MenuItem;

import androidx.annotation.NonNull;

import com.google.android.material.navigation.NavigationView;

public class NavigationViewHelper {
    public static void enableNavigation(final Context context, NavigationView view){
        view.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {

                if(item.getItemId() == R.id.nav_menu1){
                    Intent intent = new Intent(context, MainActivity.class);

                    // 싱글탑 플래그 (무조건 1개) 기존것 사용용
                   intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

                    context.startActivity(intent);
                } else if (item.getItemId() == R.id.nav_menu2){
                    Intent intent = new Intent(context, SubActivity.class);

                    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

                    context.startActivity(intent);
                }

                return true;
            }
        });
    }
}

 플래그도 추가하자

 

 

MainActivity

package com.jaybon.navigationintentex01;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;

import com.google.android.material.navigation.NavigationView;

public class MainActivity extends AppCompatActivity {

    // 아래 두가지는 무조건 만들자
    private static final String TAG = "Main_Activity";
    private Context mContext = MainActivity.this;

    private NavigationView nav;


    @Override
    protected void onNewIntent(Intent intent) { // 싱글탑 플래그일 경우 사용하던 것을 또 사용
        super.onNewIntent(intent);
        Log.d(TAG, "onNewIntent: 호출됨");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 컴포넌트들 가져오기
        init();

        // 네비게이션 메뉴들에 리스너 추가
        NavigationViewHelper.enableNavigation(mContext, nav);

    }

    private void init(){
        nav = findViewById(R.id.nav1);
    }
}

 

 

현재페이지를 또가면 

 

--------------------

영화 포스터 앱 만들기(미완)

 

https://blog.naver.com/getinthere/221694612498

 

안드로이드 3강 - (1) ListView와 GridView와 어댑터를 이용한 영화 포스터 앱 만들기

1. 리스트뷰 연습1+클릭이벤트- ListTest1 프로젝트activity_main.xmlMainActivity.java2. 리스트뷰 연...

blog.naver.com

 

 

토스트 - 컨텍스트 이용

스낵바 - 뷰를 이용하여 컨텍스트를 찾음

대화상자 - 컨텍스트가 필요 없다 ( 새로운 윈도우가 뜬다 )

 

뷰만 알면 컨텍스트를 알 수 있다

 

 

 

 

Test

더보기

package com.jaybon.listviewex01;

import android.content.Context;
import android.widget.Toast;

public class Test {

public static void callToast(Context context){

Toast.makeText(context, "안녕", Toast.LENGTH_SHORT).show();

}

}

 

MainActivity

package com.jaybon.listviewex01;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private Context mContext = MainActivity.this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Test.callToast(mContext);

    }
}

 

 

 

-------------------------------------

 

스낵바는 뷰 위에 

 

 

Test

package com.jaybon.listviewex01;

import android.content.Context;
import android.widget.Toast;

public class Test {

    public static void callToast(Context context){

        Toast.makeText(context, "안녕", Toast.LENGTH_SHORT).show();

    }

}

 

MainActivity

package com.jaybon.listviewex01;

        import androidx.appcompat.app.AppCompatActivity;

        import android.content.Context;
        import android.os.Bundle;
        import android.widget.TextView;
        import android.widget.Toast;

        import com.google.android.material.snackbar.Snackbar;

public class MainActivity extends AppCompatActivity {

    private TextView tv1;

    private Context mContext = MainActivity.this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv1 = findViewById(R.id.tv1);


        // 토스트
        Test.callToast(mContext);

        // 스넥바에 텍스트뷰를 넣어보았다
        Snackbar.make(tv1, "Text to display", Snackbar.LENGTH_LONG).show();

    }
}

 

 

225페이지

 

------------

액티비티 매니저 서비스

애플리케이션 컨텍스트 - 모든 액티비티 컨텍스트를 가지고 있다// 아이디를 가지고 있다

액티비티 컨텍스트 - 액티비티 내의 모든 내용을 가지고 있다

뷰 - 역으로 액티비티 컨텍스트, 애플리케이션 컨텍스트를 알 수 있다

getApplicationContext()

 

컨텐트 프로바이더 - 다른앱이 들고 있는 데이터에 접근 할 수 있다.

내 휴대폰 안의 앱들이 데이터를 공유하도록 하게 하는 것
(연락처에 접근하여 전화번호를 가져오거나 하는 기능)

 

-------

뷰모델 - 여러가지 뷰가 섞인 것

------

 

알림 대화상자

package com.jaybon.listviewex01;

        import androidx.appcompat.app.AlertDialog;
        import androidx.appcompat.app.AppCompatActivity;

        import android.content.Context;
        import android.content.DialogInterface;
        import android.os.Bundle;
        import android.view.View;
        import android.widget.Button;
        import android.widget.TextView;
        import android.widget.Toast;

        import com.google.android.material.snackbar.Snackbar;

public class MainActivity extends AppCompatActivity {

    private TextView tv1;
    private Button button;

    private Context mContext = MainActivity.this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv1 = findViewById(R.id.tv1);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showMessage();
            }
        });


        // 토스트
        Test.callToast(mContext);

        // 스넥바에 텍스트뷰를 넣어보았다
        Snackbar.make(tv1, "Text to display", Snackbar.LENGTH_LONG).show();

    }

    private void showMessage(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("안내");
        builder.setMessage("종료하시겠습니까?");

        builder.setPositiveButton("예", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String message = "예 버튼이 눌렸습니다.";
                tv1.setText(message);
            }
        });

        builder.setNeutralButton("취소", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String message = "취소 버튼이 눌렸습니다.";
                tv1.setText(message);
            }
        });

        builder.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String message = "아니오 버튼이 눌렸습니다.";
                tv1.setText(message);
            }
        });

        AlertDialog dialog = builder.create();
        dialog.show();

    }
}

 

 

------------------

 

리스트 뷰

 

영화포스터사진.zip
0.41MB

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

 

 

 

지금 스크롤뷰가 아닌 리스트뷰를 쓰는 이유

스크롤 뷰는 건수를 다 출력하기 때문에 부하가 너무많다

향상된 버전이 리스트 뷰

리스트 뷰는 화면의 높이를 계산하고 하나의 아이템의 높이를 계산한다

이후에는 리사이클러 뷰를 사용한다 (스크롤뷰 -> 리스트뷰 -> 리사이클러뷰)

 

-----------

어댑터패턴 - 강제성이 없다

-----------

 

어댑터가 들고 있는 것
- 전체 데이터의 컬렉션 (전체 사이즈, 인덱스 번호)
- 화면사이즈와 아이템의 사이즈
- 역할 - 스크롤을 올렸다 내렸다 할때마다 생성된 디자인에다가 데이터를 입혀준다
- 메모리관리

 

스크롤뷰는 리스트뷰에 어댑터 패턴을 쓸 수 있도록 한 것이다

 

MainActivity

package com.jaybon.movie;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";
    private Context mContext = MainActivity.this;
    private ListView listView;

    private ArrayAdapter<String> adapter; // 나중에는 안쓴다
    private List<String> mid = Arrays.asList(
            "가","나","다","라","마","바","사","아",
            "가","나","다","라","마","바","사","아",
            "가","나","다","라","마","바","사","아",
            "가","나","다","라","마","바","사","아"
    );

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView = findViewById(R.id.list_view);

        // 어댑터는 액티비티/  디자인(사이즈) / 데이터
        adapter = new ArrayAdapter<>(mContext, android.R.layout.simple_list_item_1, mid);
        listView.setAdapter(adapter);

    }
}

 

 

15개의 인플레이터 된 데이터가 있다 (화면에 보이지 않는 것들은 화면을 그쪽으로 이동하면 인플레이터 된다)

 

----------------

 

item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="99dp"
        android:text="의미없음"
        android:textSize="30sp"
        android:textStyle="bold"
        android:gravity="center"/>

    <View
        android:layout_below="@id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="1"
        android:background="@android:color/darker_gray"/>

</RelativeLayout>

 

 

 

SingleAdapter

package com.jaybon.movie;

import android.text.Layout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import org.w3c.dom.Text;

import java.util.ArrayList;
import java.util.List;

public class SingleAdapter extends BaseAdapter {

    private static final String TAG = "SingleAdapter";
    
    // 데이터
    private List<String> items = new ArrayList<>();

    // 모든건수 받기
    public void addItems(List<String> items){
        this.items = items;
    }

    // 한건씩 받기 생략
    
    // getCount과 getItem는 필수!

    @Override
    public int getCount() { // 데이터 사이즈, 최초에 화면을 몇건 만들 것인지
        Log.d(TAG, "getCount: ");
        return items.size();
    }

    @Override
    public Object getItem(int position) { // 아이템 가져오기
        Log.d(TAG, "getItem: ");
        return items.get(position);
    }

    @Override
    public long getItemId(int position) {
        Log.d(TAG, "getItemId: ");
        return 0;
    }


    // 집중!
    @Override
    public View getView(int position, View convertView, ViewGroup parent) { // 포지션값은 제일 밑에 값
        Log.d(TAG, "getView: " +position);

        LayoutInflater inflater = LayoutInflater.from(parent.getContext()); // 인플레이터
        View itemView = inflater.inflate(R.layout.item, parent, false); //아이템뷰
        TextView tv = itemView.findViewById(R.id.tv_title); //뷰에서 텍스트뷰 찾기
        tv.setText(getItem(position).toString());

        return itemView;
    }
}

 

MainActivity

package com.jaybon.movie;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";
    private Context mContext = MainActivity.this;
    private ListView listView;
    private  SingleAdapter adapter;

    private List<String> mid = Arrays.asList(
            "가","나","다","라","마","바","사","아",
            "가","나","다","라","마","바","사","아",
            "가","나","다","라","마","바","사","아",
            "가","나","다","라","마","바","사","아"
    );

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView = findViewById(R.id.list_view);
        adapter = new SingleAdapter();
        adapter.addItems(mid);
        listView.setAdapter(adapter);

    }
}

 

 

 

 

 

 

 

 

+ Recent posts