@Controller
@RequiredArgsConstructor
public class AccountController {

    private final SignUpFormValidator signUpFormValidator;
    private final AccountRepository accountRepository;
    private final ConsoleMailSender javaMailSender;

    @InitBinder("signUpForm")
    public void initBinder(WebDataBinder webDataBinder) {
        webDataBinder.addValidators(signUpFormValidator);
    }

    @GetMapping("/sign-up")
    public String signUp(Model model) {
        model.addAttribute(new SignUpForm());
        return "account/sign-up";
    }

    @PostMapping("/sign-up")
    public String signUpSubmit(@Valid SignUpForm signUpForm, Errors errors) {
        if (errors.hasErrors()) {
            return "account/sign-up";
        }

        Account account = Account.builder()
                .email(signUpForm.getEmail())
                .nickname(signUpForm.getNickname())
                .password(signUpForm.getPassword()) // TODO encoding 해야함
                .studyCreatedByWeb(true)
                .studyEnrollmentResultByWeb(true)
                .studyUpdatedByWeb(true)
                .build();
        Account newAccount = accountRepository.save(account);

        newAccount.generateEmailCheckToken();
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(newAccount.getEmail());
        mailMessage.setSubject("스터디올래, 회원 가입 인증");
        mailMessage.setText("/check-email-token?token="+ newAccount.getEmailCheckToken() +
                "&email=" + newAccount.getEmail());
        javaMailSender.send(mailMessage);

        return "redirect:/";
    }
}

@PostMapping /sign-up으로 form submit 전송에대한 Validate를 실행합니다.
SignUpForm같은 객체로 받을 경우 @ModelAttribute를 사용해야하지만 기본적으로 생략해도 됩니다.
@Valid 어노테이션으로 Validate를 진행합니다.

@Data
public class SignUpForm {

    @NotBlank
    @Length(min = 3, max = 20)
    @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-zA-Z\\d_-]{3,20}$")
    private String nickname;

    @Email
    @NotBlank
    private String email;

    @NotBlank
    @Length(min = 8, max = 50)
    private String password;
}

@Valid 어노테이션시의 로직을 실행할 어노테이션입니다. 각각의 요소는 비어있으면 안되기 때문에 @NotBlank를 사용합니다.
비슷한 어노테이션으로 @NotNull과 @NotEmpty가 있습니다.

@NotNull의 경우 말 그대로 null만 제외하고 @NotEmpty의 경우 null, ""을 제외합니다. 그치만 " "와 같은 띄어쓰기는 허용합니다.
@NotBlank는 null, "", " " 모두 제외시킵니다.
SignUpForm에 들어가는 nickname, email, password 셋 모두 빈칸도 허용할 수 없으므로 @NotBlank를 사용합니다.

@Length의 경우 길이, @Pattern(regexp = "")는 정규식으로 패턴을 정규화하는 것입니다. @Email을 email의 형식인지를 Validate하는 어노테이션입니다.

@Component
@RequiredArgsConstructor
public class SignUpFormValidator implements Validator {

    private final AccountRepository accountRepository;

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.isAssignableFrom(SignUpForm.class);
    }

    @Override
    public void validate(Object target, Errors errors) {
        SignUpForm signUpForm = (SignUpForm) target;
        if (accountRepository.existsByEmail(signUpForm.getEmail())) {
            errors.rejectValue("email", "invalid.email", new Object[]{signUpForm.getEmail()}, "이미 사용중인 이메일입니다.");
        }

        if (accountRepository.existsByNickname(signUpForm.getNickname())) {
            errors.rejectValue("nickname", "invalid.nickname", new Object[]{signUpForm.getNickname()}, "이미 사용중인 닉네임입니다.");
        }
    }
}

AccountController에 주입된 SignUpFormValidator의 경우 springframework에서 제공하는 Validator객체를 implements해 사용합니다.

supports(Class<?> clazz), validate(Object target, Errors errors)을 Override해서 사용합니다.

Spring Security를 사용하며 Config를 정의하는 클래스는 그 동안 WebSecurityConfigurerAdapter를 extends해서 configure(HttpSecurity)를 Override해 사용해왔지만 deprecated된 후에는 SecurityFilterChain을 return 하는 메서드를 만들어 @Bean 을 등록해서 써야합니다.


기존코드
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                .mvcMatchers("/", "/login", "/sign-up", "/check-email", "/check-email-token",
                        "/email-login", "/check-email-login", "/login-link").permitAll()
                .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                .anyRequest().authenticated();
    }
}
SecurityFilterChain을 리턴하는 코드
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authz) -> authz
                        .mvcMatchers("/", "/login", "/sign-up", "/check-email", "/check-email-token",
                                "/email-login", "/check-email-login", "/login-link").permitAll()
                        .mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
                        .anyRequest().authenticated());
        return http.build();
    }

}

위와같이 바꿔 쓸 수 있습니다.

현재 백기선선생님의 "스프링과 JPA 기반 웹 애플리케이션 개발"의 강의를 보고 있지만 강의 시간상 Deprecated되기 전 강의인가 보네요


@EnableWebSecurity에 @Configuration이 있는데 왜 둘 다 선언하신건 왜 일까용..

위 이미지는 버튼 이벤트로 돌아가게했지만 코드상으로는 setInterval()을 적용해 자동으로 넘어갑니다.

 

<body>
    <div id="slider-container">
      <div id="left-slider">공지사항</div>
      <div style="font-size: 28px; font-weight: 400; color: #bebebe">|</div>
      <div class="right-slider">
        <div>
          <a href="#">
            1 번째 공지사항입니다.
            adsfdsfadfsdsaffdsdfsdfasadfsafsdafsdadsf(200123.,13)asdfa
          </a>
        </div>
        <div><a href="#">2 번째 공지사항입니다.</a></div>
        <div><a href="#">3 번째 공지사항입니다.</a></div>
      </div>
    </div>
    <button id="button">앞으로</button>
  </body>
  <script>
    const rightSlider = document.querySelector(".right-slider");
    const slidesLength = rightSlider.querySelectorAll("div").length;
    let sliderIndex = 0;

    const slide = () => {
      sliderIndex++;

      if (sliderIndex > slidesLength - 1) {
        sliderIndex = 0;
      }

      rightSlider.style.transform = `translateY(-${sliderIndex * 100}%)`;
    };

    setInterval(slide, 3000);

    document.getElementById("button").addEventListener("click", () => {
      sliderIndex++;

      if (sliderIndex > slidesLength - 1) {
        sliderIndex = 0;
      }

      rightSlider.style.transform = `translateY(-${sliderIndex * 100}%)`;
    });
    const sliderContainer = document.getElementsByClassName("slider-container");
  </script>
#slider-container {
  width: 650px;
  overflow: hidden;
  height: 60px;
  position: absolute;
  left: 100px;
  top: 100px;
  display: flex;
  align-items: center;
  border: 1px solid #ebebeb;
  border-radius: 6px;
  font-size: 18px;
  background-color: white;
}

#left-slider {
  width: 20%;
  text-align: center;
}

.right-slider {
  height: 100%;
  width: 80%;
  margin-left: 20px;
  transition: transform 0.5s ease-in-out;
}

.right-slider > div {
  height: 100%;
}

a {
  text-decoration: none;
}

a:link {
  color: black;
  text-decoration: none;
}
  a:visited {
  color: black;
  text-decoration: none;
}
  a:hover {
  color: black;
  text-decoration: none;
}
a:active {
  color: black;
  text-decoration: none;
}

< ChameleonDev >('XCAPE'이하 'ChameleonDev')은(는) 「개인정보 보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보 처리방침을 수립·공개합니다.

○ 이 개인정보처리방침은 2022년 6월 14부터 적용됩니다.

 

제1조(개인정보의 처리 목적)

< ChameleonDev >('XCAPE'이하 'ChameleonDev')은(는) 다음의 목적을 위하여 개인정보를 처리합니다. 처리하고 있는 개인정보는 다음의 목적 이외의 용도로는 이용되지 않으며 이용 목적이 변경되는 경우에는 「개인정보 보호법」 제18조에 따라 별도의 동의를 받는 등 필요한 조치를 이행할 예정입니다.



제2조(개인정보의 처리 및 보유 기간)

 < ChameleonDev >은(는) 법령에 따른 개인정보 보유·이용기간 또는 정보주체로부터 개인정보를 수집 시에 동의받은 개인정보 보유·이용기간 내에서 개인정보를 처리·보유합니다.

② 각각의 개인정보 처리 및 보유 기간은 다음과 같습니다.



제3조(처리하는 개인정보의 항목)

 < ChameleonDev >은(는) 다음의 개인정보 항목을 처리하고 있습니다.



제4조(개인정보의 파기절차 및 파기방법)


① < ChameleonDev > 은(는) 개인정보 보유기간의 경과, 처리목적 달성 등 개인정보가 불필요하게 되었을 때에는 지체없이 해당 개인정보를 파기합니다.

② 정보주체로부터 동의받은 개인정보 보유기간이 경과하거나 처리목적이 달성되었음에도 불구하고 다른 법령에 따라 개인정보를 계속 보존하여야 하는 경우에는, 해당 개인정보를 별도의 데이터베이스(DB)로 옮기거나 보관장소를 달리하여 보존합니다.
1. 법령 근거 :
2. 보존하는 개인정보 항목 : 계좌정보, 거래날짜

③ 개인정보 파기의 절차 및 방법은 다음과 같습니다.
1. 파기절차
< ChameleonDev > 은(는) 파기 사유가 발생한 개인정보를 선정하고, < ChameleonDev > 의 개인정보 보호책임자의 승인을 받아 개인정보를 파기합니다.

2. 파기방법

전자적 파일 형태의 정보는 기록을 재생할 수 없는 기술적 방법을 사용합니다



제5조(정보주체와 법정대리인의 권리·의무 및 그 행사방법에 관한 사항)



① 정보주체는 ChameleonDev에 대해 언제든지 개인정보 열람·정정·삭제·처리정지 요구 등의 권리를 행사할 수 있습니다.

② 제1항에 따른 권리 행사는ChameleonDev에 대해 「개인정보 보호법」 시행령 제41조제1항에 따라 서면, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 ChameleonDev은(는) 이에 대해 지체 없이 조치하겠습니다.

③ 제1항에 따른 권리 행사는 정보주체의 법정대리인이나 위임을 받은 자 등 대리인을 통하여 하실 수 있습니다.이 경우 “개인정보 처리 방법에 관한 고시(제2020-7호)” 별지 제11호 서식에 따른 위임장을 제출하셔야 합니다.

④ 개인정보 열람 및 처리정지 요구는 「개인정보 보호법」 제35조 제4항, 제37조 제2항에 의하여 정보주체의 권리가 제한 될 수 있습니다.

⑤ 개인정보의 정정 및 삭제 요구는 다른 법령에서 그 개인정보가 수집 대상으로 명시되어 있는 경우에는 그 삭제를 요구할 수 없습니다.

⑥ ChameleonDev은(는) 정보주체 권리에 따른 열람의 요구, 정정·삭제의 요구, 처리정지의 요구 시 열람 등 요구를 한 자가 본인이거나 정당한 대리인인지를 확인합니다.



제6조(개인정보의 안전성 확보조치에 관한 사항)

< ChameleonDev >은(는) 개인정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.

1. 해킹 등에 대비한 기술적 대책
<ChameleonDev>('ChameleonDev')은 해킹이나 컴퓨터 바이러스 등에 의한 개인정보 유출 및 훼손을 막기 위하여 보안프로그램을 설치하고 주기적인 갱신·점검을 하며 외부로부터 접근이 통제된 구역에 시스템을 설치하고 기술적/물리적으로 감시 및 차단하고 있습니다.



제7조(개인정보를 자동으로 수집하는 장치의 설치·운영 및 그 거부에 관한 사항)



ChameleonDev 은(는) 정보주체의 이용정보를 저장하고 수시로 불러오는 ‘쿠키(cookie)’를 사용하지 않습니다.

제8조 (개인정보 보호책임자에 관한 사항)

 ChameleonDev 은(는) 개인정보 처리에 관한 업무를 총괄해서 책임지고, 개인정보 처리와 관련한 정보주체의 불만처리 및 피해구제 등을 위하여 아래와 같이 개인정보 보호책임자를 지정하고 있습니다.

  • ▶ 개인정보 보호책임자
  • 성명 :고주영
  • 직책 :개발
  • 직급 :대표
  • 연락처 :01027471135, kapella000@gmail.com,

※ 개인정보 보호 담당부서로 연결됩니다.

  • ▶ 개인정보 보호 담당부서
  • 부서명 :
  • 담당자 :
  • 연락처 :, ,

② 정보주체께서는 ChameleonDev 의 서비스(또는 사업)을 이용하시면서 발생한 모든 개인정보 보호 관련 문의, 불만처리, 피해구제 등에 관한 사항을 개인정보 보호책임자 및 담당부서로 문의하실 수 있습니다. ChameleonDev 은(는) 정보주체의 문의에 대해 지체 없이 답변 및 처리해드릴 것입니다.

제9조(개인정보의 열람청구를 접수·처리하는 부서)
정보주체는 「개인정보 보호법」 제35조에 따른 개인정보의 열람 청구를 아래의 부서에 할 수 있습니다.
< ChameleonDev >은(는) 정보주체의 개인정보 열람청구가 신속하게 처리되도록 노력하겠습니다.

  • ▶ 개인정보 열람청구 접수·처리 부서
  • 부서명 :
  • 담당자 :
  • 연락처 : , ,



제10조(정보주체의 권익침해에 대한 구제방법)



정보주체는 개인정보침해로 인한 구제를 받기 위하여 개인정보분쟁조정위원회, 한국인터넷진흥원 개인정보침해신고센터 등에 분쟁해결이나 상담 등을 신청할 수 있습니다. 이 밖에 기타 개인정보침해의 신고, 상담에 대하여는 아래의 기관에 문의하시기 바랍니다.

1. 개인정보분쟁조정위원회 : (국번없이) 1833-6972 (www.kopico.go.kr)
2. 개인정보침해신고센터 : (국번없이) 118 (privacy.kisa.or.kr)
3. 대검찰청 : (국번없이) 1301 (www.spo.go.kr)
4. 경찰청 : (국번없이) 182 (ecrm.cyber.go.kr)

「개인정보보호법」제35조(개인정보의 열람), 제36조(개인정보의 정정·삭제), 제37조(개인정보의 처리정지 등)의 규정에 의한 요구에 대 하여 공공기관의 장이 행한 처분 또는 부작위로 인하여 권리 또는 이익의 침해를 받은 자는 행정심판법이 정하는 바에 따라 행정심판을 청구할 수 있습니다.

※ 행정심판에 대해 자세한 사항은 중앙행정심판위원회(www.simpan.go.kr) 홈페이지를 참고하시기 바랍니다.

제11조(개인정보 처리방침 변경)

 

① 이 개인정보처리방침은 2022년 6월 14부터 적용됩니다.

② 이전의 개인정보 처리방침은 아래에서 확인하실 수 있습니다.

예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)

예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)

예시 ) - 20XX. X. X ~ 20XX. X. X 적용 (클릭)

뒤집은 소수

N개의 자연수가 입력되면 각 자연수를 뒤집은 후 그 뒤집은 수가 소수이면 그 소수를 출력하 는 프로그램을 작성하세요. 예를 들어 32를 뒤집으면 23이고, 23은 소수이다. 그러면 23을 출 력한다. 단 910를 뒤집으면 19로 숫자화 해야 한다. 첫 자리부터의 연속된 0은 무시한다.

입력설명
첫 줄에 자연수의 개수 N(3<=N<=100)이 주어지고, 그 다음 줄에 N개의 자연수가 주어진다. 각 자연수의 크기는 100,000를 넘지 않는다.

출력설명
첫 줄에 뒤집은 소수를 출력합니다. 출력순서는 입력된 순서대로 출력합니다.

입력예제 1
9
32 55 62 20 250 370 200 30 100

출력예제 1 23 2 73 2 3


최대 주어지는 숫자의 크기만큼 소수배열을 만들고 각 숫자를 뒤집은 인덱스가 0이면 소수

 

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] numbers = new int[n];

        for (int i = 0; i < n; i++) {
            numbers[i] = scanner.nextInt();
        }

        int[] primeNumbers = getPrimeNumbers();

        for (int i = 0; i < n; i++) {
            int reverseNumber = makeReverseNumber(numbers[i]);
            if (primeNumbers[reverseNumber] == 0) {
                System.out.print(reverseNumber + " ");
            }
        }

    }
	
    // 최대 주어지는 숫자의 범위까지 소수 판별하는 메소드
    private static int[] getPrimeNumbers() {
        int[] primeNumbers = new int[100001];
        primeNumbers[0] = primeNumbers[1] = 1;

        for (int i = 2; i <= 10000; i++) {
            for (int j = i * i; j <= 100000; j += i) {
                if (primeNumbers[i] == 0) {
                    primeNumbers[j] = 1;
                }
            }
        }

        return primeNumbers;
    }
	
    // 숫자를 뒤집는 메소드
    private static int makeReverseNumber(int number) {
        int reverseNumber = 0;

        while (number > 0) {
            reverseNumber = reverseNumber * 10 + (number % 10);
            number /= 10;
        }

        return reverseNumber;
    }
}

https://firework-ham.tistory.com/8

 

[JAVA] 소수 구하는 알고리즘 : 에라토스테네스의 체

소수 구하는 알고리즘으로 유명한 에라토스테네스의 체입니다. 고대 그리스의 수학자 에라토스테네스가 만들어 낸 소수를 찾는 방법으로 코딩 알고리즘에서 소수를 구할 때도 이 방법을 사용

firework-ham.tistory.com

이 블로그를 참고했습니다.

'알고리즘' 카테고리의 다른 글

[DFS] 섬나라 아일랜드  (0) 2022.02.27

주사위 세개

 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초 128 MB 50638 25380 22472 51.493%

문제

1에서부터 6까지의 눈을 가진 3개의 주사위를 던져서 다음과 같은 규칙에 따라 상금을 받는 게임이 있다. 

  1. 같은 눈이 3개가 나오면 10,000원+(같은 눈)×1,000원의 상금을 받게 된다. 
  2. 같은 눈이 2개만 나오는 경우에는 1,000원+(같은 눈)×100원의 상금을 받게 된다. 
  3. 모두 다른 눈이 나오는 경우에는 (그 중 가장 큰 눈)×100원의 상금을 받게 된다.  

예를 들어, 3개의 눈 3, 3, 6이 주어지면 상금은 1,000+3×100으로 계산되어 1,300원을 받게 된다. 또 3개의 눈이 2, 2, 2로 주어지면 10,000+2×1,000 으로 계산되어 12,000원을 받게 된다. 3개의 눈이 6, 2, 5로 주어지면 그중 가장 큰 값이 6이므로 6×100으로 계산되어 600원을 상금으로 받게 된다.

3개 주사위의 나온 눈이 주어질 때, 상금을 계산하는 프로그램을 작성 하시오.

입력

첫째 줄에 3개의 눈이 빈칸을 사이에 두고 각각 주어진다. 

출력

첫째 줄에 게임의 상금을 출력 한다.

예제 입력 1 복사

3 3 6

예제 출력 1 복사

1300

예제 입력 2 복사

2 2 2

예제 출력 2 복사

12000

예제 입력 3 복사

6 2 5

예제 출력 3 복사

600

 

import java.util.Arrays;
import java.util.Scanner;

public class Question_2480 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[] numbers = new int[3];

        for (int i = 0; i < 3; i++) {
            numbers[i] = scanner.nextInt();
        }
        Arrays.sort(numbers);

        if (numbers[0] == numbers[2]) {
            System.out.println(10000 + numbers[0] * 1000);
        } else if (numbers[0] == numbers[1] || numbers[1] == numbers[2]) {
            System.out.println(1000 + numbers[1] * 100);
        } else {
            System.out.println(100 * numbers[2]);
        }
    }
}

첨엔 세개의 경우의수만 했는데 numbers[1] == numbers[2]의 경우의 수를 생각치 못해서 세 번이나 틀림 ㅠ

'알고리즘 > 여러가지' 카테고리의 다른 글

unordered_map VS map c++  (1) 2021.05.08
버블정렬 (Bubble Sort)  (0) 2021.04.27
선택정렬 (Selection Sort)  (0) 2021.04.27
ArrayList는 List라는 인터페이스를 구현한 구현객체

라는 말을 들어보셧을겁니다.

List interface는 선언만해놓고 구현은 하지않은 틀이고, ArrayList는 List라는 틀로 구현한 구현체

ArrayList만 아니라 LinkedList, Vector도 생성가능

// 사용불가 List<String> strList = new List<>();

List<String> strList = new LinkedList<>();
List<String> strList = new ArrayList<>();
List<String> strList = new Vector<>();

 

LinkedList의 가장 큰 특징은 데이터, 다음 대상의 주소를 가지고 있음

ArrayList, Vector는 Resizable-array(가변배열) 크기를 정해놓으면 변경 못하는 배열과 달리 가변적으로 크기가 생성됨

multi-thread환경에서는 Vector가 권장됨

'Java 공부' 카테고리의 다른 글

== equals 뭐가 달라?  (0) 2021.10.22
Optional 객체 다루기  (1) 2021.10.05
String , new String  (4) 2021.10.01
자바 공식문서 보는방법.  (0) 2021.03.29
그래들 스프링 이클립스에 임포트하기  (0) 2021.02.25

drag and drop의 기능이 필요해 라이브러리를 찾던 중 react-beautiful-dnd 라이브러리를 찾았는데 클래스형으로는 예제가 있지만 함수형은 없는 것같아 검색했습니다.

코드샌드박스에 누군가 잘 올려준 게시물 덕분에 해결

https://codesandbox.io/s/9z5tn?file=/src/index.js 

 

vertical-list - CodeSandbox

vertical-list using react, react-beautiful-dnd, react-dom

codesandbox.io

 

저같은 경우 App.js에서 <PageList /> 에게 props로 [pageList, setPageList]를 넘겨주고 이를 pageList 컴포넌트에서 받아 react-beautiful-dnd로 렌더하는 식입니다.

import React, { useState } from "react";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";

// a little function to help us with reordering the result
const reorder = (list, startIndex, endIndex) => {
  const result = Array.from(list);
  const [removed] = result.splice(startIndex, 1);
  result.splice(endIndex, 0, removed);

  return result;
};

const grid = 8;

const getItemStyle = (isDragging, draggableStyle) => ({
  // some basic styles to make the items look a bit nicer
  userSelect: "none",
  padding: grid * 2,
  margin: `0 0 ${grid}px 0`,

  // change background colour if dragging
  background: isDragging ? "lightgreen" : "grey",

  // styles we need to apply on draggables
  ...draggableStyle,
});

const getListStyle = (isDraggingOver) => ({
  background: isDraggingOver ? "lightblue" : "lightgrey",
  padding: grid,
  width: 500,
  position: "relative",
});

const queryAttr = "data-rbd-drag-handle-draggable-id";

const InputList = (props) => {
  const [placeholderProps, setPlaceholderProps] = useState({});
  const { pageList, setPageList } = props;
  // const [items, setItems] = useState(getItems(10));

  const onDragEnd = (result) => {
    // dropped outside the list
    if (!result.destination) {
      return;
    }

    setPlaceholderProps({});
    setPageList((items) =>
      reorder(items, result.source.index, result.destination.index)
    );
  };

  const onDragUpdate = (update) => {
    if (!update.destination) {
      return;
    }
    const draggableId = update.draggableId;
    const destinationIndex = update.destination.index;

    const domQuery = `[${queryAttr}='${draggableId}']`;
    const draggedDOM = document.querySelector(domQuery);
    if (!draggedDOM) {
      return;
    }
    const { clientHeight, clientWidth } = draggedDOM;

    const clientY =
      parseFloat(window.getComputedStyle(draggedDOM.parentNode).paddingTop) +
      [...draggedDOM.parentNode.children]
        .slice(0, destinationIndex)
        .reduce((total, curr) => {
          const style = curr.currentStyle || window.getComputedStyle(curr);
          const marginBottom = parseFloat(style.marginBottom);
          return total + curr.clientHeight + marginBottom;
        }, 0);

    setPlaceholderProps({
      clientHeight,
      clientWidth,
      clientY,
      clientX: parseFloat(
        window.getComputedStyle(draggedDOM.parentNode).paddingLeft
      ),
    });
  };

  // Normally you would want to split things out into separate components.
  // But in this example everything is just done in one place for simplicity
  return (
    <DragDropContext onDragEnd={onDragEnd} onDragUpdate={onDragUpdate}>
      <Droppable droppableId="droppable">
        {(provided, snapshot) => (
          <div
            {...provided.droppableProps}
            ref={provided.innerRef}
            style={getListStyle(snapshot.isDraggingOver)}
          >
            {pageList.map((item, index) => (
              <Draggable
                key={`item${item.index}`}
                draggableId={`item-${item.index}`}
                index={index}
              >
                {(provided, snapshot) => (
                  <div
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    style={getItemStyle(
                      snapshot.isDragging,
                      provided.draggableProps.style
                    )}
                  >
                    <label>index: {index}</label>
                    <br />
                    <label>item.index: {item.index}</label>
                    <br />
                    <label>component: {item.component}</label>
                    <div>
                      <img src={item.url} alt="" height="200" />
                      <br />
                      <label>url: {item.url}</label>
                    </div>
                  </div>
                )}
              </Draggable>
            ))}

            {provided.placeholder}
            {/* <CustomPlaceholder snapshot={snapshot} /> */}
            <div
              style={{
                position: "absolute",
                top: placeholderProps.clientY,
                left: placeholderProps.clientX,
                height: placeholderProps.clientHeight,
                background: "tomato",
                width: placeholderProps.clientWidth,
              }}
            />
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );
};

export default InputList;

바꾼건 <Droppable /> 안에 items.map() -> pageList.map() 이랑 <Draggable />안에 key, draggableId, 내용물 정도입니다.

draggableId에다가 숫자만 넣으면 requires string~~~ 이라는 경고문이 나오는데 이는 앞에 문자열을 붙여주면 해결.

사진이 제대로 변함!!!!

react-native-video

 

GitHub - react-native-video/react-native-video: A <Video /> component for react-native

A <Video /> component for react-native. Contribute to react-native-video/react-native-video development by creating an account on GitHub.

github.com

 

import {View, StyleSheet} from 'react-native';
import React from 'react';
import Video from 'react-native-video';

const SecondPage = () => {
  return (
    <View style={styles.container}>
      <Video
        source={{
          uri: 'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
        }}
        style={styles.backgroundVideo}
        fullscreen={true}
        resizeMode={'contain'}
        repeat={true}
        controls={true}
      />
    </View>
  );
};

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'white',
  },
  backgroundVideo: {
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
  },
});

export default SecondPage;
render error null is not an object (evaluating 'RCTVideoInstance.Constants')

비디오를 테스트하기위해 react-native-video 라이브러리를 설치하고 실행하려는데 위와 같은 에러가 나왔다.

검색해보니 깃헙의 이슈에도 나와같은 증상인 사람들이 여럿잇으나 하라는대로 해도 되지 않았는데 결국 해결은 함

allprojects {
    repositories {
    	...
    	jcenter()
    }
}

android/build.gradle에 jcenter()을 추가하니까 됐당 왜인지는 모름... 재생은 잘됨

 

+ Recent posts